diff --git a/eoc/chapter3.py b/eoc/chapter3.py index cd0fe64d..e2244ab0 100644 --- a/eoc/chapter3.py +++ b/eoc/chapter3.py @@ -1659,6 +1659,8 @@ class OneOverX(PiCreatureScene, GraphScene): self.y_max = self.y_axis_height/self.unit_length def construct(self): + self.force_skipping() + self.introduce_function() self.introduce_puddle() self.introduce_graph() @@ -1704,7 +1706,7 @@ class OneOverX(PiCreatureScene, GraphScene): run_time = 2 ) self.dither() - self.play(*self.get_bubble_fade_anims()) + self.play(RemovePiCreatureBubble(self.pi_creature)) def introduce_puddle(self): rect_group = self.get_rectangle_group(self.start_x_value) @@ -1805,8 +1807,16 @@ class OneOverX(PiCreatureScene, GraphScene): df_brace = Brace(h_lines, RIGHT, buff = 0) df_label = df_brace.get_text("$d\\left(\\frac{1}{x}\\right)$") df_brace.add(df_label) - minus_sign = TexMobject("-") - minus_sign.move_to(df_label, LEFT) + + negative = TextMobject("Negative") + negative.highlight(RED) + negative.next_to(df_label, UP+RIGHT) + negative.shift(RIGHT) + negative_arrow = Arrow( + negative.get_left(), + df_label.get_corner(UP+RIGHT), + color = RED + ) area_changes = VGroup() point_pairs = [ @@ -1888,9 +1898,10 @@ class OneOverX(PiCreatureScene, GraphScene): ShowCreation(area_lost_arrow) ) self.dither() + self.revert_to_original_skipping_status()### self.play( - Write(minus_sign), - df_label.next_to, minus_sign, RIGHT, SMALL_BUFF + Write(negative), + ShowCreation(negative_arrow) ) self.dither() self.play( diff --git a/eoc/chapter4.py b/eoc/chapter4.py index dcfcc18c..58078a99 100644 --- a/eoc/chapter4.py +++ b/eoc/chapter4.py @@ -25,1113 +25,2227 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * -from topics.common_scenes import OpeningQuote, PatreonThanks +from topics.common_scenes import OpeningQuote, PatreonThanks from eoc.graph_scene import * -class ExpFootnoteOpeningQuote(OpeningQuote): +SINE_COLOR = BLUE +X_SQUARED_COLOR = GREEN +SUM_COLOR = YELLOW +PRODUCT_COLOR = YELLOW + +class Chapter4OpeningQuote(OpeningQuote): CONFIG = { "quote" : [ - "Who has not been amazed to learn that the function", - "$y = e^x$,", "like a phoenix rising again from its own", - "ashes, is its own derivative?", + "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." ], - "highlighted_quote_terms" : { - "$y = e^x$" : MAROON_B - }, - "author" : "Francois le Lionnais" + "author" : "(Anonymous professor)" } -class LastVideo(TeacherStudentsScene): +class TransitionFromLastVideo(TeacherStudentsScene): def construct(self): + simple_rules = VGroup(*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) - known_formulas = VGroup(*map(TexMobject, [ - "\\frac{d(x^n)}{dx} = nx^{n-1}", - "\\frac{d(\\sin(x))}{dx} = \\cos(x)", - ])) - known_formulas.arrange_submobjects( - DOWN, buff = MED_LARGE_BUFF, - ) - known_formulas.scale_to_fit_height(2.5) - exp_question = TexMobject("2^x", ", 7^x", ", e^x", " ???") - - last_video_brace = Brace(last_video) - known_formulas.next_to(last_video_brace, DOWN) - last_video_brace.save_state() - last_video_brace.shift(3*LEFT) - last_video_brace.set_fill(opacity = 0) - + #Simple rules self.add(series) self.play( - last_video_brace.restore, - last_video.highlight, YELLOW, - self.get_teacher().change_mode, "raise_right_hand", - ) - self.play(Write(known_formulas)) - self.dither() - self.student_says( - exp_question, student_index = -1, - added_anims = [self.get_teacher().change_mode, "pondering"] + FadeIn(brace), + last_video.highlight, 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.dither() self.dither(2) - self.play(known_formulas.replace, last_video) - self.play(last_video_brace.next_to, this_video, DOWN) + self.play(simple_rules.replace, last_video) self.play( - last_video.restore, + last_video.restore, + Animation(simple_rules), + brace.next_to, this_video, DOWN, this_video.highlight, YELLOW ) - self.play( - exp_question.next_to, last_video_brace, DOWN, - FadeOut(self.get_students()[-1].bubble), - ) - self.change_student_modes( - *["pondering"]*3, - look_at_arg = exp_question - ) - self.dither() -class PopulationSizeGraphVsPopulationMassGraph(Scene): - def construct(self): - pass - -class DoublingPopulation(PiCreatureScene): - CONFIG = { - "time_color" : YELLOW, - "pi_creature_grid_dimensions" : (8, 8), - "pi_creature_grid_height" : 6, - } - - def construct(self): - self.remove(self.get_pi_creatures()) - self.introduce_expression() - self.introduce_pi_creatures() - self.count_through_days() - self.ask_about_dM_dt() - self.growth_per_day() - self.relate_growth_rate_to_pop_size() - - def introduce_expression(self): - f_x = TexMobject("f(x)", "=", "2^x") - f_t = TexMobject("f(t)", "=", "2^t") - P_t = TexMobject("P(t)", "=", "2^t") - M_t = TexMobject("M(t)", "=", "2^t") - functions = VGroup(f_x, f_t, P_t, M_t) - for function in functions: - function.scale(1.2) - function.to_corner(UP+LEFT) - for function in functions[1:]: - for i, j in (0, 2), (2, 1): - function[i][j].highlight(self.time_color) - - t_expression = TexMobject("t", "=", "\\text{Time (in days)}") - t_expression.to_corner(UP+RIGHT) - t_expression[0].highlight(self.time_color) - - pop_brace, mass_brace = [ - Brace(function[0], DOWN) - for function in P_t, M_t - ] - for brace, word in (pop_brace, "size"), (mass_brace, "mass"): - text = brace.get_text("Population %s"%word) - text.to_edge(LEFT) - brace.text = text - - self.play(Write(f_x)) - self.dither() + #Combination rules self.play( - Transform(f_x, f_t), - FadeIn( - t_expression, - run_time = 2, - submobject_mode = "lagged_start" - ) - ) - self.play(Transform(f_x, P_t)) - self.play( - GrowFromCenter(pop_brace), - Write(pop_brace.text, run_time = 2) + Write(combination_rules), + *[ + ApplyMethod(pi.change_mode, "confused") + for pi in self.get_students() + ] ) self.dither(2) - - self.function = f_x - self.pop_brace = pop_brace - self.t_expression = t_expression - self.mass_function = M_t - self.mass_brace = mass_brace - - def introduce_pi_creatures(self): - creatures = self.get_pi_creatures() - total_num_days = self.get_num_days() - num_start_days = 4 - - self.reset() - for x in range(num_start_days): - self.let_one_day_pass() - self.dither() - self.play( - Transform(self.function, self.mass_function), - Transform(self.pop_brace, self.mass_brace), - Transform(self.pop_brace.text, self.mass_brace.text), - ) - self.dither() - for x in range(total_num_days-num_start_days): - self.let_one_day_pass() - self.dither() - self.joint_blink(shuffle = False) - self.dither() - - def count_through_days(self): - self.reset() - brace = self.get_population_size_descriptor() - days_to_let_pass = 3 - - self.play(GrowFromCenter(brace)) - self.dither() - for x in range(days_to_let_pass): - self.let_one_day_pass() - new_brace = self.get_population_size_descriptor() - self.play(Transform(brace, new_brace)) - self.dither() - - self.population_size_descriptor = brace - - def ask_about_dM_dt(self): - dM_dt_question = TexMobject("{dM", "\\over dt}", "=", "???") - dM, dt, equals, q_marks = dM_dt_question - dM_dt_question.next_to(self.function, DOWN, buff = LARGE_BUFF) - dM_dt_question.to_edge(LEFT) - - self.play( - FadeOut(self.pop_brace), - FadeOut(self.pop_brace.text), - Write(dM_dt_question) - ) - self.dither(3) - for mob in dM, dt: - self.play(Indicate(mob)) - self.dither() - - self.dM_dt_question = dM_dt_question - - def growth_per_day(self): - day_to_day, frac = self.get_from_day_to_day_label() - - self.play( - FadeOut(self.dM_dt_question), - FadeOut(self.population_size_descriptor), - FadeIn(day_to_day) - ) - rect = self.let_day_pass_and_highlight_new_creatures(frac) - - for x in range(2): - new_day_to_day, new_frac = self.get_from_day_to_day_label() - self.play(*map(FadeOut, [rect, frac])) - frac = new_frac - self.play(Transform(day_to_day, new_day_to_day)) - rect = self.let_day_pass_and_highlight_new_creatures(frac) - self.play(*map(FadeOut, [rect, frac, day_to_day])) - - def let_day_pass_and_highlight_new_creatures(self, frac): - num_new_creatures = 2**self.get_curr_day() - - self.let_one_day_pass() - new_creatures = VGroup( - *self.get_on_screen_pi_creatures()[-num_new_creatures:] - ) - rect = Rectangle( - color = GREEN, - fill_color = BLUE, - fill_opacity = 0.3, - ) - rect.replace(new_creatures, stretch = True) - rect.stretch_to_fit_height(rect.get_height()+MED_SMALL_BUFF) - rect.stretch_to_fit_width(rect.get_width()+MED_SMALL_BUFF) - self.play(DrawBorderThenFill(rect)) - self.play(Write(frac)) - self.dither() - return rect - - def relate_growth_rate_to_pop_size(self): - false_deriv = TexMobject( - "{d(2^t) ", "\\over dt}", "= 2^t" - ) - difference_eq = TexMobject( - "{ {2^{t+1} - 2^t} \\over", "1}", "= 2^t" - ) - real_deriv = TexMobject( - "{ {2^{t+dt} - 2^t} \\over", "dt}", "= \\, ???" - ) - VGroup( - false_deriv[0][3], - false_deriv[2][-1], - difference_eq[0][1], - difference_eq[0][-2], - difference_eq[2][-1], - difference_eq[2][-1], - real_deriv[0][1], - real_deriv[0][-2], - ).highlight(YELLOW) - VGroup( - difference_eq[0][3], - difference_eq[1][-1], - real_deriv[0][3], - real_deriv[0][4], - real_deriv[1][-2], - real_deriv[1][-1], - ).highlight(GREEN) - - expressions = [false_deriv, difference_eq, real_deriv] - text_arg_list = [ - ("Tempting", "...",), - ("Rate of change", "\\\\ over one full day"), - ("Rate of change", "\\\\ in a small time"), - ] - for expression, text_args in zip(expressions, text_arg_list): - expression.next_to( - self.function, DOWN, - buff = LARGE_BUFF, - aligned_edge = LEFT, - ) - expression.brace = Brace(expression, DOWN) - expression.brace_text = expression.brace.get_text(*text_args) - - time = self.t_expression[-1] - new_time = TexMobject("3") - new_time.move_to(time, LEFT) - - fading_creatures = VGroup(*self.get_on_screen_pi_creatures()[8:]) - - - self.play(*map(FadeIn, [ - false_deriv, false_deriv.brace, false_deriv.brace_text - ])) - self.dither() - self.play( - Transform(time, new_time), - FadeOut(fading_creatures) - ) - self.dither() - for x in range(3): - self.let_one_day_pass(run_time = 2) - self.dither(2) - - for expression in difference_eq, real_deriv: - expression.brace_text[1].highlight(GREEN) + 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.highlight, YELLOW + ] self.play( - Transform(false_deriv, expression), - Transform(false_deriv.brace, expression.brace), - Transform(false_deriv.brace_text, expression.brace_text), - ) - self.dither(3) - self.reset() - for x in range(self.get_num_days()): - self.let_one_day_pass() - self.dither() - - rect = Rectangle(color = YELLOW) - rect.replace(real_deriv) - rect.stretch_to_fit_width(rect.get_width()+MED_SMALL_BUFF) - rect.stretch_to_fit_height(rect.get_height()+MED_SMALL_BUFF) - self.play(*map(FadeOut, [ - false_deriv.brace, false_deriv.brace_text - ])) - self.play(ShowCreation(rect)) - self.play(*[ - ApplyFunction( - lambda pi : pi.change_mode("pondering").look_at(real_deriv), - pi, - run_time = 2, - rate_func = squish_rate_func(smooth, a, a+0.5) - ) - for pi in self.get_pi_creatures() - for a in [0.5*random.random()] - ]) - self.dither(3) - - ########### - - def create_pi_creatures(self): - width, height = self.pi_creature_grid_dimensions - creature_array = VGroup(*[ - VGroup(*[ - PiCreature(mode = "plain") - for y in range(height) - ]).arrange_submobjects(UP, buff = MED_LARGE_BUFF) - for x in range(width) - ]).arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF) - creatures = VGroup(*it.chain(*creature_array)) - creatures.scale_to_fit_height(self.pi_creature_grid_height) - creatures.to_corner(DOWN+RIGHT) - - colors = color_gradient([BLUE, GREEN, GREY_BROWN], len(creatures)) - random.shuffle(colors) - for creature, color in zip(creatures, colors): - creature.set_color(color) - - return creatures - - def reset(self): - time = self.t_expression[-1] - faders = [time] + list(self.get_on_screen_pi_creatures()) - new_time = TexMobject("0") - new_time.next_to(self.t_expression[-2], RIGHT) - first_creature = self.get_pi_creatures()[0] - - self.play(*map(FadeOut, faders)) - self.play(*map(FadeIn, [first_creature, new_time])) - self.t_expression.submobjects[-1] = new_time - - def let_one_day_pass(self, run_time = 2): - all_creatures = self.get_pi_creatures() - on_screen_creatures = self.get_on_screen_pi_creatures() - low_i = len(on_screen_creatures) - high_i = min(2*low_i, len(all_creatures)) - new_creatures = VGroup(*all_creatures[low_i:high_i]) - - to_children_anims = [] - growing_anims = [] - for old_pi, pi in zip(on_screen_creatures, new_creatures): - pi.save_state() - child = pi.copy() - child.scale(0.25, about_point = child.get_bottom()) - child.eyes.scale(1.5, about_point = child.eyes.get_bottom()) - pi.move_to(old_pi) - pi.set_fill(opacity = 0) - - index = list(new_creatures).index(pi) - prop = float(index)/len(new_creatures) - alpha = np.clip(len(new_creatures)/8.0, 0, 0.5) - rate_func = squish_rate_func( - smooth, alpha*prop, alpha*prop+(1-alpha) - ) - - to_child_anim = Transform(pi, child, rate_func = rate_func) - to_child_anim.update(1) - growing_anim = ApplyMethod(pi.restore, rate_func = rate_func) - to_child_anim.update(0) - - to_children_anims.append(to_child_anim) - growing_anims.append(growing_anim) - - time = self.t_expression[-1] - total_new_creatures = len(on_screen_creatures) + len(new_creatures) - new_time = TexMobject(str(int(np.log2(total_new_creatures)))) - new_time.move_to(time, LEFT) - - growing_anims.append(Transform(time, new_time)) - - self.play(*to_children_anims, run_time = run_time/2.0) - self.play(*growing_anims, run_time = run_time/2.0) - - def get_num_pi_creatures_on_screen(self): - mobjects = self.get_mobjects() - return sum([ - pi in mobjects for pi in self.get_pi_creatures() - ]) - - def get_population_size_descriptor(self): - on_screen_creatures = self.get_on_screen_pi_creatures() - brace = Brace(on_screen_creatures, LEFT) - n = len(on_screen_creatures) - label = brace.get_text( - "$2^%d$"%int(np.log2(n)), - "$=%d$"%n, - ) - brace.add(label) - return brace - - def get_num_days(self): - x, y = self.pi_creature_grid_dimensions - return int(np.log2(x*y)) - - def get_curr_day(self): - return int(np.log2(len(self.get_on_screen_pi_creatures()))) - - def get_from_day_to_day_label(self): - curr_day = self.get_curr_day() - top_words = TextMobject( - "From day", str(curr_day), - "to", str(curr_day+1), ":" - ) - top_words.scale_to_fit_width(4) - top_words.next_to( - self.function, DOWN, - buff = MED_LARGE_BUFF, - aligned_edge = LEFT, - ) - top_words[1].highlight(GREEN) - - bottom_words = TexMobject( - str(2**curr_day), - "\\text{ creatures}", "\\over {1 \\text{ day}}" - ) - bottom_words[0].highlight(GREEN) - bottom_words.next_to(top_words, DOWN, buff = MED_LARGE_BUFF) - - return top_words, bottom_words - -class GraphOfTwoToT(GraphScene): - CONFIG = { - "x_axis_label" : "$t$", - "y_axis_label" : "$M$", - "x_labeled_nums" : range(1, 7), - "y_labeled_nums" : range(8, 40, 8), - "x_max" : 6, - "y_min" : 0, - "y_max" : 32, - "y_tick_frequency" : 2, - "graph_origin" : 2.5*DOWN + 5*LEFT, - } - def construct(self): - self.setup_axes() - example_t = 3 - graph = self.get_graph(lambda t : 2**t, color = BLUE_C) - self.graph = graph - graph_label = self.get_graph_label( - graph, "M(t) = 2^t", - direction = LEFT, - ) - label_group = self.get_label_group(example_t) - v_line, brace, height_label, ss_group, slope_label = label_group - self.animate_secant_slope_group_change( - ss_group, - target_dx = 1, - run_time = 0 - ) - self.remove(ss_group) - - #Draw graph and revert to tangent - self.play(ShowCreation(graph)) - self.play(Write(graph_label)) - self.dither() - self.play(Write(ss_group)) - self.dither() - for target_dx in 0.01, 1, 0.01: - self.animate_secant_slope_group_change( - ss_group, - target_dx = target_dx - ) - self.dither() - - #Mark up with values - - self.play(ShowCreation(v_line)) - self.play( - GrowFromCenter(brace), - Write(height_label, run_time = 1) - ) - self.dither() - self.play( - FadeIn( - slope_label, - run_time = 4, + interior.highlight, YELLOW, + *added_anims, submobject_mode = "lagged_start" - ), - ReplacementTransform( - height_label.copy(), - slope_label.get_part_by_tex("2^") ) - ) + self.dither() self.dither() - #Vary value - threes = VGroup(height_label[1], slope_label[2][1]) - ts = VGroup(*[ - TexMobject("t").highlight(YELLOW).scale(0.75).move_to(three) - for three in threes - ]) - self.play(Transform(threes, ts)) - - alt_example_t = example_t+1 - def update_label_group(group, alpha): - t = interpolate(example_t, alt_example_t, alpha) - new_group = self.get_label_group(t) - Transform(group, new_group).update(1) - for t, three in zip(ts, threes): - t.move_to(three) - Transform(threes, ts).update(1) - return group - - self.play(UpdateFromAlphaFunc( - label_group, update_label_group, - run_time = 3, - )) - self.play(UpdateFromAlphaFunc( - label_group, update_label_group, - run_time = 3, - rate_func = lambda t : 1 - 1.5*smooth(t) - )) - - def get_label_group(self, t): - graph = self.graph - - v_line = self.get_vertical_line_to_graph( - t, graph, - color = YELLOW, + #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:]), ) - brace = Brace(v_line, RIGHT) - height_label = brace.get_text("$2^%d$"%t) - - ss_group = self.get_secant_slope_group( - t, graph, dx = 0.01, - df_label = "dM", - dx_label = "dt", - dx_line_color = GREEN, - secant_line_color = RED, + 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:]), ) - slope_label = TexMobject( - "\\text{Slope}", "=", - "2^%d"%t, - "(%.7f\\dots)"%np.log(2) + 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, SPACE_HEIGHT*UP, UP, + combination_rules.to_edge, UP, ) - slope_label.next_to( - ss_group.secant_line.point_from_proportion(0.65), - DOWN+RIGHT, - buff = 0 - ) - slope_label.highlight_by_tex("Slope", RED) - return VGroup( - v_line, brace, height_label, - ss_group, slope_label - ) - -class SimpleGraphOfTwoToT(GraphOfTwoToT): - CONFIG = { - "x_axis_label" : "", - "y_axis_label" : "", - } - def construct(self): - self.setup_axes() - func = lambda t : 2**t - graph = self.get_graph(func) - line_pairs = VGroup() - for x in 1, 2, 3, 4, 5: - point = self.coords_to_point(x, func(x)) - x_axis_point = self.coords_to_point(x, 0) - y_axis_point = self.coords_to_point(0, func(x)) - line_pairs.add(VGroup( - DashedLine(x_axis_point, point), - DashedLine(y_axis_point, point), - )) - - - self.play(ShowCreation(graph, run_time = 2)) - for pair in line_pairs: - self.play(ShowCreation(pair)) - self.dither() - -class AnalyzeExponentRatio(PiCreatureScene): - CONFIG = { - "base" : 2, - "base_str" : "2", - } - def construct(self): - base_str = self.base_str - - func_def = TexMobject("M(", "t", ")", "= ", "%s^"%base_str, "t") - func_def.to_corner(UP+LEFT) - self.add(func_def) - - ratio = TexMobject( - "{ {%s^"%base_str, "{t", "+", "dt}", "-", - "%s^"%base_str, "t}", - "\\over \\,", "dt}" - ) - ratio.shift(UP+LEFT) - - lhs = TexMobject("{dM", "\\over \\,", "dt}", "(", "t", ")", "=") - lhs.next_to(ratio, LEFT) - - - two_to_t_plus_dt = VGroup(*ratio[:4]) - two_to_t = VGroup(*ratio[5:7]) - two_to_t_two_to_dt = TexMobject( - "%s^"%base_str, "t", - "%s^"%base_str, "{dt}" - ) - two_to_t_two_to_dt.move_to(two_to_t_plus_dt, DOWN+LEFT) - exp_prop_brace = Brace(two_to_t_two_to_dt, UP) - - one = TexMobject("1") - one.move_to(ratio[5], DOWN) - lp, rp = parens = TexMobject("()") - parens.stretch(1.3, 1) - parens.scale_to_fit_height(ratio.get_height()) - lp.next_to(ratio, LEFT, buff = 0) - rp.next_to(ratio, RIGHT, buff = 0) - - extracted_two_to_t = TexMobject("%s^"%base_str, "t") - extracted_two_to_t.next_to(lp, LEFT, buff = SMALL_BUFF) - - expressions = [ - ratio, two_to_t_two_to_dt, - extracted_two_to_t, lhs, func_def + pairs = [ + (subtraction, decomposed_subtraction), + (division, decomposed_division) ] - for expression in expressions: - expression.highlight_by_tex("t", YELLOW) - expression.highlight_by_tex("dt", GREEN) + 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.dither() + answer[1].highlight(GREEN) + self.play( + Transform(question, answer), + self.get_teacher().change_mode, "hooray", + self.get_students()[2].change_mode, "plain", + ) + self.dither() + self.play(FadeOut(question)) - #Apply exponential property - self.play( - Write(ratio), Write(lhs), - self.pi_creature.change_mode, "raise_right_hand" + #Monstrous expression + monster = TexMobject( + "\\Big(", + "e^{\\sin(x)} \\cdot", + "\\cos\\Big(", + "\\frac{1}{x^3}", + " + x^3", + "\\Big)", + "\\Big)^4" ) - self.dither(2) - self.play( - two_to_t_plus_dt.next_to, exp_prop_brace, UP, - self.pi_creature.change_mode, "pondering" - ) - self.play( - ReplacementTransform( - two_to_t_plus_dt.copy(), two_to_t_two_to_dt, - run_time = 2, - path_arc = np.pi, - ), - FadeIn(exp_prop_brace) - ) - self.dither(2) + 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.dither() + self.change_student_modes(*["happy"]*3) + words = map(TextMobject, [ + "composition", "product", + "composition", "sum", + "composition" + ]) - #Talk about exponential property - add_exp_rect, mult_rect = rects = [ - Rectangle( - stroke_color = BLUE, - stroke_width = 2, - ).replace(mob).scale_in_place(1.1) - for mob in [ - VGroup(*two_to_t_plus_dt[1:]), - two_to_t_two_to_dt + for word, part in zip(words, reversed(parts)): + word.highlight(YELLOW) + word.next_to(monster, UP) + self.play( + FadeIn(word), + part.scale_in_place, 1.2, + part.highlight, YELLOW + ) + self.dither() + self.play(*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() ] - ] - words = VGroup(*[ - TextMobject(s, " ideas") - for s in "Additive", "Multiplicative" - ]) - words[0].move_to(words[1], LEFT) - words.highlight(BLUE) - words.next_to(two_to_t_plus_dt, RIGHT, buff = 1.5*LARGE_BUFF) - arrows = VGroup(*[ - Arrow(word.get_left(), rect, color = words.get_color()) - for word, rect in zip(words, rects) - ]) - - self.play(ShowCreation(add_exp_rect)) - self.dither() - self.play(ReplacementTransform( - add_exp_rect.copy(), mult_rect - )) - self.dither() - self.change_mode("happy") - self.play(Write(words[0], run_time = 2)) - self.play(ShowCreation(arrows[0])) - self.dither() - self.play( - Transform(*words), - Transform(*arrows), ) self.dither(2) - self.play(*map(FadeOut, [ - words[0], arrows[0], add_exp_rect, mult_rect, - two_to_t_plus_dt, exp_prop_brace, + +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.highlight(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.dither() + +class ComingUp(Scene): + def construct(self): + rect = Rectangle(height = 9, width = 16) + rect.set_stroke(WHITE) + rect.scale_to_fit_height(2*SPACE_HEIGHT-2) + title = TextMobject("Coming up...") + title.to_edge(UP) + rect.next_to(title, DOWN) + + self.play(Write(title)) + self.play(ShowCreation(rect)) + self.dither() + +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].highlight(SINE_COLOR) + formula[6].highlight(SINE_COLOR) + formula[3].highlight(X_SQUARED_COLOR) + formula[8].highlight(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.dither(2) + for indices in [(1, 2, 3), (6,), (7, 8)]: + self.play(*[ + ReplacementTransform( + specific[i].copy(), general[i] + ) + for i in indices + ]) + self.dither() + + #Highlight parts + for i in 1, 3, -1, 6, 8: + if i < 0: + self.dither() + else: + part = specific[i] + self.play( + part.highlight, YELLOW, + part.scale_in_place, 1.2, + rate_func = there_and_back + ) + self.dither() + +class SumRule(GraphScene): + CONFIG = { + "x_labeled_nums" : [], + "y_labeled_nums" : [], + "y_axis_label" : "", + "x_max" : 4, + "x_axis_width" : 2*SPACE_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.highlight_by_tex("f(x)", SUM_COLOR) + func_mob.highlight_by_tex("\\sin(x)", SINE_COLOR) + func_mob.highlight_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.dither() + + 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.dither() + 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.dither() + + self.play(ShowCreation(sum_graph)) + self.dither() + + 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(*map(MoveToTarget, [ + graph_parts, self.graph_labels + ])) + self.dither() + + 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(*filter( + lambda line : line not in example_v_lines, + it.chain(*v_line_sets) + )) + 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.highlight(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.dither() + self.play( + GrowFromCenter(sine_lines[0].brace), + Write(sine_lines[0].brace_text), + ) + self.dither() + self.play( + sine_lines[0].shift, UP+4*LEFT, + sine_lines[0].brace_anim, + parabola_lines[0].move_to, sine_lines[0], DOWN + ) + self.dither() + parabola_lines[0].brace_anim.update(1) + self.play( + GrowFromCenter(parabola_lines[0].brace), + Write(parabola_lines[0].brace_text), + ) + self.dither() + self.play(*it.chain(*[ + [line.restore, line.brace_anim] + for line in example_v_lines ])) - #Factor out 2^t - self.play(*[ - FadeIn( - mob, - run_time = 2, - rate_func = squish_rate_func(smooth, 0.5, 1) + ## Nudged_lines + self.play( + Write(label_groups[1]), + *it.chain(*[ + [line.restore, line.set_stroke, None, 4] + for line in nudged_v_lines + ]) + ) + self.dither() + for line in nudged_v_lines: + self.play( + GrowFromCenter(line.brace), + Write(line.brace_text) ) - for mob in one, lp, rp - ] + [ - ReplacementTransform( - mob, extracted_two_to_t, - path_arc = np.pi/2, - run_time = 2, - ) - for mob in two_to_t, VGroup(*two_to_t_two_to_dt[:2]) - ] + [ - lhs.next_to, extracted_two_to_t, LEFT + self.dither() + + 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.highlight(SUM_COLOR) + d_sine.highlight(SINE_COLOR) + d_x_squared.highlight(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.dither() + 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.dither() + + 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) ]) - self.change_mode("pondering") - shifter = VGroup(ratio[4], one, *two_to_t_two_to_dt[2:]) - stretcher = VGroup(lp, ratio[7], rp) + 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.dither() self.play( - shifter.next_to, ratio[7], UP, - stretcher.stretch_in_place, 0.9, 0 + ReplacementTransform(brace_text.copy(), target_tex), + Write(added_tex) ) - self.dither(2) + return VGroup(h_lines, brace, brace_text) - #Ask about dt -> 0 - brace = Brace(VGroup(extracted_two_to_t, ratio), DOWN) - alt_brace = Brace(parens, DOWN) - dt_to_zero = TexMobject("dt", "\\to 0") - dt_to_zero.highlight_by_tex("dt", GREEN) - dt_to_zero.next_to(brace, DOWN) - - self.play(GrowFromCenter(brace)) - self.play(Write(dt_to_zero)) - self.dither(2) - - #Who cares - randy = Randolph() - randy.scale(0.7) - randy.to_edge(DOWN) - - self.play( - FadeIn(randy), - self.pi_creature.change_mode, "plain", + def expand_derivative(self): + expanded_deriv = TexMobject( + "df", "=", "\\cos(x)", "\\,dx", "+", "2x", "\\,dx" ) - self.play(PiCreatureSays( - randy, "Who cares?", - bubble_kwargs = {"direction" : LEFT}, - target_mode = "angry", - )) - self.dither(2) - self.play( - RemovePiCreatureBubble(randy), - FadeOut(randy), - self.pi_creature.change_mode, "hooray", - self.pi_creature.look_at, parens + expanded_deriv.highlight_by_tex("df", SUM_COLOR) + VGroup(*expanded_deriv[2:4]).highlight(SINE_COLOR) + VGroup(*expanded_deriv[5:7]).highlight(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].highlight(SUM_COLOR) + rearranged_deriv[3].highlight(SINE_COLOR) + rearranged_deriv[5].highlight(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( - Transform(brace, alt_brace), - dt_to_zero.next_to, alt_brace, DOWN + FadeIn( + background_rect, + rate_func = squish_rate_func(smooth, 0.6, 1) + ), + Write(expanded_deriv) ) self.dither() - #Highlight separation - rects = [ - Rectangle( - stroke_color = color, - stroke_width = 2, - ).replace(mob, stretch = True).scale_in_place(1.1) - for mob, color in [ - (VGroup(parens, dt_to_zero), GREEN), - (extracted_two_to_t, YELLOW), - ] + tex_group_pairs = [ + ("\\cos(0.5)dx", self.d_sine_lines), + ("2(0.5)dx", self.d_x_squared_lines), ] - self.play(ShowCreation(rects[0])) - self.dither(2) - self.play(ReplacementTransform(rects[0].copy(), rects[1])) - self.change_mode("happy") - self.dither() - self.play(*map(FadeOut, rects)) - - #Plug in specific values - static_constant = self.try_specific_dt_values() - constant = static_constant.copy() - - #Replace with actual constant - limit_term = VGroup( - brace, dt_to_zero, ratio[4], one, rects[0], - *ratio[7:]+two_to_t_two_to_dt[2:] - ) - self.play(FadeIn(rects[0])) - self.play(limit_term.to_corner, DOWN+LEFT) - self.play( - lp.stretch, 0.5, 1, - lp.stretch, 0.8, 0, - lp.next_to, extracted_two_to_t[0], RIGHT, - rp.stretch, 0.5, 1, - rp.stretch, 0.8, 0, - rp.next_to, lp, RIGHT, SMALL_BUFF, - rp.shift, constant.get_width()*RIGHT, - constant.next_to, extracted_two_to_t[0], RIGHT, MED_LARGE_BUFF - ) - self.dither() - self.change_mode("confused") - self.dither() - - #Indicate distinction between dt group and t group again - for mob in limit_term, extracted_two_to_t: - self.play(FocusOn(mob)) - self.play(Indicate(mob)) - self.dither() - - #hold_final_value - derivative = VGroup( - lhs, extracted_two_to_t, parens, constant - ) - func_def_rhs = VGroup(*func_def[-2:]).copy() - func_lp, func_rp = func_parens = TexMobject("()") - func_parens.set_fill(opacity = 0) - func_lp.next_to(func_def_rhs[0], LEFT, buff = 0) - func_rp.next_to(func_lp, RIGHT, buff = func_def_rhs.get_width()) - func_def_rhs.add(func_parens) - M = lhs[0][1] - - self.play( - FadeOut(M), - func_def_rhs.move_to, M, LEFT, - func_def_rhs.set_fill, None, 1, - ) - lhs[0].submobjects[1] = func_def_rhs - self.dither() - self.play( - derivative.next_to, self.pi_creature, UP, - derivative.to_edge, RIGHT, - self.pi_creature.change_mode, "raise_right_hand" - ) - self.dither(2) - for mob in extracted_two_to_t, constant: - self.play(Indicate(mob)) - self.dither() - self.dither(2) - - def try_specific_dt_values(self): - expressions = [] - for num_zeros in [1, 2, 4, 7]: - dt_str = "0." + num_zeros*"0" + "1" - dt_num = float(dt_str) - output_num = (self.base**dt_num - 1) / dt_num - output_str = "%.7f\\dots"%output_num - - expression = TexMobject( - "{%s^"%self.base_str, "{%s}"%dt_str, "-1", - "\\over \\,", "%s}"%dt_str, - "=", output_str + def indicate(mob): + self.play( + mob.highlight, YELLOW, + mob.scale_in_place, 1.2, + rate_func = there_and_back ) - expression.highlight_by_tex(dt_str, GREEN) - expression.highlight_by_tex(output_str, BLUE) - expression.to_corner(UP+RIGHT) - expressions.append(expression) + 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.highlight(old_label.get_color()) - curr_expression = expressions[0] - self.play( - Write(curr_expression), - self.pi_creature.change_mode, "pondering" - ) - self.dither(2) - for expression in expressions[1:]: - self.play(Transform(curr_expression, expression)) - self.dither(2) - return curr_expression[-1] - -class ExponentRatioWithThree(AnalyzeExponentRatio): - CONFIG = { - "base" : 3, - "base_str" : "3", - } - -class ExponentRatioWithSeven(AnalyzeExponentRatio): - CONFIG = { - "base" : 7, - "base_str" : "7", - } - -class ExponentRatioWithE(AnalyzeExponentRatio): - CONFIG = { - "base" : np.exp(1), - "base_str" : "e", - } - -class AskAboutConstantOne(TeacherStudentsScene): - def construct(self): - note = TexMobject( - "{ d(a^", "t", ")", "\\over \\,", "dt}", - "=", "a^", "t", "(\\text{Some constant})" - ) - note.highlight_by_tex("t", YELLOW) - note.highlight_by_tex("dt", GREEN) - note.highlight_by_tex("constant", BLUE) - note.to_corner(UP+LEFT) - self.add(note) - - self.student_says( - "Is there a base where\\\\", - "that constant is 1?" - ) - self.change_student_modes( - "pondering", "raise_right_hand", "thinking", - # look_at_arg = self.get_students()[1].bubble - ) - self.dither(2) - self.play(FadeOut(note[-1], run_time = 3)) + self.play(FocusOn(old_label)) + indicate(old_label) + self.dither() + self.play(FadeOut(old_label)) + self.play(FadeIn(new_label)) + self.dither() + indicate(dx) + self.dither() + indicate(pre_dx) + self.dither() self.dither() + self.play(*[ + Transform( + expanded_deriv[i], rearranged_deriv[j], + path_arc = -np.pi/2 + ) + for i, j in enumerate(deriv_target_indices) + ]) + self.dither() + +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].highlight(SINE_COLOR) + wrong_product_rule[3].highlight(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( - "There is!\\\\", - "$e = 2.71828\\dots$", - target_mode = "hooray" + "Products are a bit different", + target_mode = "sassy" ) + self.dither(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.highlight, RED + ] + ) + self.dither() + 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.dither(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.dither() + + everything = VGroup(*filter( + lambda m : not m.is_subpath, + self.get_mobjects() + )) + words = TextMobject("Not the best visualization") + words.scale(1.5) + words.shift(SPACE_HEIGHT*UP/2) + words.add_background_rectangle() + words.highlight(RED) + self.play( + everything.fade, + Write(words) + ) + self.dither() + +class ConfusedMorty(Scene): + def construct(self): + morty = Mortimer() + self.add(morty) + self.dither() + self.play(morty.change_mode, "confused") + self.play(Blink(morty)) + self.dither(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.dither() + for mob in self.x_slider: + self.play(Write(mob, run_time = 1)) + self.dither() + for new_x in 0.5, 2, self.default_x: + self.animate_x_change( + new_x, run_time = 2 + ) + self.dither() + + 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.highlight(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.scale_to_fit_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.dither() + self.play(*it.chain( + map(FadeOut, [y_axis, graph, label, v_line]), + [Animation(x_axis)] + )) + self.dither() + for x in 1, 0.5, self.default_x: + self.animate_x_change(x) + self.dither() + + 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].highlight(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.dither() + + 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(*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.dither() + 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.dither() + + 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.highlight(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.highlight(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.highlight(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.dither() + + self.highlight_right_boxes() + self.highlight_bottom_boxes() + self.describe_bottom_box(bottom_box_area) + self.describe_right_box(right_box_area) + self.ignore_corner() + + # self.add(deriv) + + def highlight_boxes_and_label(self, boxes, label): + boxes.save_state() + label.save_state() + + self.play(GrowFromCenter(label)) + self.play( + boxes.highlight, RED, + label.highlight, RED, + ) + self.play( + label[1].scale_in_place, 1.1, + rate_func = there_and_back + ) + self.play(boxes.restore, label.restore) + self.dither() + + def highlight_right_boxes(self): + self.highlight_boxes_and_label( + VGroup(*self.df_boxes[1:]), + self.df_box_labels[0] + ) + + def highlight_bottom_boxes(self): + self.highlight_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(*filter( + lambda m : m not in [bottom_box, top_label, right_label], + self.full_box_parts + )) + faders.save_state() + + self.play(faders.fade, 0.8) + self.dither() + 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.dither() + 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(*filter( + lambda m : m not in [right_box, top_label, right_label], + self.full_box_parts + )) + faders.save_state() + + self.play(faders.fade, 0.8) + self.dither() + 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.dither() + 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.highlight(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.highlight, RED, + corner_copy.highlight, RED, + ) + self.dither() + self.play(Write(words), ShowCreation(arrow)) + self.dither() + self.play(*map(FadeOut, [words, arrow, corner_copy])) + self.dither() + corner_copy.highlight(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.dither() + + 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.highlight(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.highlight, YELLOW, + rate_func = there_and_back + ) + + + for index in 6, 3: + self.deriv.submobjects.insert( + index+1, self.deriv[index].copy() + ) + non_deriv_indices = 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.dither() + indicate(top_part) + self.dither() + self.play(ReplacementTransform(top_part, bottom_part)) + self.dither() + 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.dither() + 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.dither() + for index in 0, 1, 3, 4, 6, 7: + indicate(final_deriv[index]) + self.dither() + + 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.dither() + + 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]).highlight(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.dither() + + ############### + + 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 = range(int(self.slider_x_max) + 1) + line = NumberLine( + x_min = 0, + x_max = self.slider_x_max, + space_unit_to_num = 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.scale_to_fit_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.highlight(RED) + self.play(Write(term)) + self.dither() + +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]).highlight(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.dither() + self.play(*map(Indicate, [left, left_words[0], left_terms[0]])) + self.dither() + self.play(*map(Indicate, [right, left_words[1], left_terms[1]])) + self.dither() + + 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.dither() + self.play(*map(Indicate, [right, right_words[1], right_terms[1]])) + self.dither() + self.play(*map(Indicate, [left, right_words[2], right_terms[2]])) + self.dither(3) + + self.play(self.get_teacher().change_mode, "shruggie") + self.dither() self.change_student_modes(*["confused"]*3) self.dither(3) -class NaturalLog(Scene): +class ConstantMultiplication(TeacherStudentsScene): def construct(self): - expressions = VGroup(*map(self.get_expression, [2, 3, 7])) - expressions.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF) - expressions.to_edge(LEFT) + question = TextMobject("What about $\\dfrac{d}{dx}(2\\sin(x))$?") + answer = TextMobject("2\\cos(x)") + self.teacher_says(question) + self.dither() + 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.dither(3) - self.play(FadeIn( - expressions, - run_time = 3, - submobject_mode = "lagged_start" +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.highlight(SINE_COLOR) + x_squared = TexMobject("h(x)", "=", "x^2") + x_squared.highlight(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].highlight(SINE_COLOR) + for i in 1, 5: + composition[i].highlight(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.dither() + 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.dither() + self.dither() + self.play( + GrowFromCenter(brace), + Write(deriv_q) + ) + self.dither() + +class ThreeLinesChainRule(ReconfigurableScene): + CONFIG = { + "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" : 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" : 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.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) + ]) + 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() + for mob in dsine_value: + self.play(Indicate(mob)) + 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(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.dither() + 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) + example.shift(RIGHT) + 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: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.dither() + self.dither() + 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( - expressions.set_fill, None, 1, + 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, - submobject_mode = "lagged_start" - ) + remover = True + )) self.dither() - for i in 0, 2, 1: - self.show_natural_loggedness(expressions[i]) - def show_natural_loggedness(self, expression): - base, constant = expression[1], expression[-3] - - log_constant, exp_constant = constant.copy(), constant.copy() - log_base, exp_base = base.copy(), base.copy() - log_equals, exp_equals = map(TexMobject, "==") - - ln = TexMobject("\\ln(2)") - log_base.move_to(ln[-2]) - ln.remove(ln[-2]) - log_equals.next_to(ln, LEFT) - log_constant.next_to(log_equals, LEFT) - log_expression = VGroup( - ln, log_constant, log_equals, log_base - ) - - e = TexMobject("e") - exp_constant.scale(0.7) - exp_constant.next_to(e, UP+RIGHT, buff = 0) - exp_base.next_to(exp_equals, RIGHT) - VGroup(exp_base, exp_equals).next_to( - VGroup(e, exp_constant), RIGHT, - aligned_edge = DOWN - ) - exp_expression = VGroup( - e, exp_constant, exp_equals, exp_base - ) - - for group, vect in (log_expression, UP), (exp_expression, DOWN): - group.to_edge(RIGHT) - group.shift(vect) - - self.play( - ReplacementTransform(base.copy(), log_base), - ReplacementTransform(constant.copy(), log_constant), - run_time = 2 - ) - self.play(Write(ln), Write(log_equals)) + #Generalize + self.play(*map(FadeIn, general[:5])) self.dither() self.play( - ReplacementTransform( - log_expression.copy(), - exp_expression, - run_time = 2, - ) + 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.dither() + 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.dither(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.dither(2) - ln_a = expression[-1] + #Name chain rule + name = TextMobject("``Chain rule''") + name.scale(1.2) + name.highlight(YELLOW) + name.to_corner(UP+LEFT) + self.play(Write(name)) + self.dither() + + #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( - FadeOut(expression[-2]), - FadeOut(constant), - ln_a.move_to, constant, LEFT, - ln_a.highlight, BLUE + 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.dither() + 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.dither() + 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.dither() + self.play(morty.change_mode, "pondering") + self.play(Blink(morty)) + self.dither() + self.play(*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(*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(*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.dither() - self.play(*map(FadeOut, [log_expression, exp_expression])) + 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.dither() + 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.highlight(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.dither(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.dither() + self.play(Blink(morty)) self.dither() - def get_expression(self, base): - expression = TexMobject( - "{d(", "%d^"%base, "t", ")", "\\over \\,", "dt}", - "=", "%d^"%base, "t", "(%.4f\\dots)"%np.log(base), +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 ) - expression.highlight_by_tex("t", YELLOW) - expression.highlight_by_tex("dt", GREEN) - expression.highlight_by_tex("\\dots", BLUE) + formulas.next_to(randy, LEFT, buff = MED_LARGE_BUFF) + formulas.shift_onto_screen() - brace = Brace(expression.get_part_by_tex("\\dots"), UP) - brace_text = brace.get_text("$\\ln(%d)$"%base) - for mob in brace, brace_text: - mob.set_fill(opacity = 0) + self.add(randy, laptop) + self.dither() + self.play(randy.change_mode, "erm") + self.play(Blink(randy)) + self.dither() + self.play(randy.change_mode, "maybe") + self.dither() + self.play(Blink(randy)) + for formula in formulas: + self.play( + Write(formula, run_time = 2), + randy.change_mode, "thinking" + ) + self.dither() - expression.add(brace, brace_text) - return expression + def create_pi_creatures(self): + return [Randolph().shift(DOWN+RIGHT)] class NextVideo(TeacherStudentsScene): def construct(self): series = VideoSeries() series.to_edge(UP) - this_video = series[3] next_video = series[4] - brace = Brace(this_video, DOWN) - this_video.save_state() - this_video.highlight(YELLOW) - this_tex = TexMobject( - "{d(", "a^t", ") \\over dt} = ", "a^t", "\\ln(a)" + pre_expression = TexMobject( + "x", "^2", "+", "y", "^2", "=", "1" ) - this_tex[1][1].highlight(YELLOW) - this_tex[3][1].highlight(YELLOW) - this_tex.next_to(brace, DOWN) + 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) - next_tex = VGroup(*map(TextMobject, [ - "Chain rule", "Product rule", "$\\vdots$" - ])) - next_tex.arrange_submobjects(DOWN) - next_tex.next_to(brace, DOWN) - next_tex.shift( - next_video.get_center()[0]*RIGHT\ - -next_tex.get_center()[0]*RIGHT - ) + 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() - self.add(series, brace, *this_tex[:3]) - self.change_student_modes( - "confused", "pondering", "erm", - look_at_arg = this_tex + 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.highlight, YELLOW, + self.get_teacher().change_mode, "raise_right_hand" ) - self.play(ReplacementTransform( - this_tex[1].copy(), this_tex[3] - )) self.dither() self.play( - Write(this_tex[4]), - ReplacementTransform( - this_tex[3][0].copy(), - this_tex[4][3], - path_arc = np.pi, - remover = True + Write(expression), + *[ + ApplyMethod(pi.change_mode, "pondering") + for pi in self.get_students() + ] + ) + self.play(FadeIn(axes)) + self.dither() + self.remove(expression) + self.play(Transform(expression, d_expression, path_arc = np.pi/2)) + self.dither() + self.play( + Rotate( + line, np.pi/4, + about_point = axes.get_center(), + rate_func = wiggle, + run_time = 3 ) ) self.dither(2) - self.play(this_tex.replace, this_video) - self.play( - brace.next_to, next_video, DOWN, - this_video.restore, - Animation(this_tex), - next_video.highlight, YELLOW, - Write(next_tex), - self.get_teacher().change_mode, "raise_right_hand" - ) - self.change_student_modes( - *["pondering"]*3, - look_at_arg = next_tex - ) - self.dither(3) -class ExpPatreonThanks(PatreonThanks): +class Chapter4Thanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Ali Yahya", "Meshal Alshammari", "CrypticSwarm ", - "Kathryn Schmiedicke", - "Nathan Pellegrin", - "Karan Bhargava", - "Justin Helps", "Ankit Agarwal", "Yu Jun", + "Shelby Doolittle", "Dave Nicponski", "Damion Kistler", "Juan Benet", @@ -1142,12 +2256,7 @@ class ExpPatreonThanks(PatreonThanks): "Derek Dai", "Joseph John Cox", "Luc Ritchie", - "Mustafa Mahdi", - "Daan Smedinga", - "Jonathan Eppele", - "Albert Nguyen", "Nils Schneider", - "Mustafa Mahdi", "Mathew Bramson", "Guido Gambardella", "Jerry Ling", @@ -1162,42 +2271,31 @@ class ExpPatreonThanks(PatreonThanks): ] } -class Thumbnail(Scene): +class Thumbnail(IntroduceProductAsArea): + CONFIG = { + "default_x" : 0.8, + "dx" : 0.05 + } def construct(self): - derivative = TexMobject( - "\\frac{d(a^t)}{dt} =", "a^t \\ln(a)" + self.x_slider = self.get_x_slider(self.default_x) + blg = self.box_label_group = self.get_box_label_group( + self.default_x ) - derivative[0][3].highlight(YELLOW) - derivative[1][1].highlight(YELLOW) - derivative[0][2].highlight(BLUE) - derivative[1][0].highlight(BLUE) - derivative[1][5].highlight(BLUE) - derivative.scale(3) - # derivative.to_edge(UP) - derivative.shift(DOWN) - - brace = Brace(Line(LEFT, RIGHT), UP) - brace.scale_to_fit_width(derivative[1].get_width()) - brace.next_to(derivative[1], UP) - question = TextMobject("Why?") - question.scale(2.5) - question.next_to(brace, UP) - - # randy = Randolph() - # randy.scale(1.3) - # randy.next_to(ORIGIN, LEFT).to_edge(DOWN) - # randy.change_mode("pondering") - - # question = TextMobject("What is $e\\,$?") - # e = question[-2] - # e.scale(1.2, about_point = e.get_bottom()) - # e.highlight(BLUE) - # question.scale(1.7) - # question.next_to(randy, RIGHT, aligned_edge = UP) - # question.shift(DOWN) - # randy.look_at(question) - - self.add(derivative, brace, question) + 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.scale_to_fit_height(2*SPACE_HEIGHT-2*MED_LARGE_BUFF) + blg.center() + self.add(blg) + + + + + + diff --git a/eoc/chapter5.py b/eoc/chapter5.py index 39dc3b2d..dcfcc18c 100644 --- a/eoc/chapter5.py +++ b/eoc/chapter5.py @@ -25,2227 +25,1113 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * - from topics.common_scenes import OpeningQuote, PatreonThanks + from eoc.graph_scene import * -SINE_COLOR = BLUE -X_SQUARED_COLOR = GREEN -SUM_COLOR = YELLOW -PRODUCT_COLOR = YELLOW - -class Chapter5OpeningQuote(OpeningQuote): +class ExpFootnoteOpeningQuote(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." + "Who has not been amazed to learn that the function", + "$y = e^x$,", "like a phoenix rising again from its own", + "ashes, is its own derivative?", ], - "author" : "(Anonymous professor)" + "highlighted_quote_terms" : { + "$y = e^x$" : MAROON_B + }, + "author" : "Francois le Lionnais" } -class TransitionFromLastVideo(TeacherStudentsScene): +class LastVideo(TeacherStudentsScene): def construct(self): - simple_rules = VGroup(*map(TexMobject, [ - "\\frac{d(x^3)}{dx} = 3x^2", - "\\frac{d(\\sin(x))}{dx} = \\cos(x)", - "\\frac{d(e^x)}{dx} = e^x", - ])) - - 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 + known_formulas = VGroup(*map(TexMobject, [ + "\\frac{d(x^n)}{dx} = nx^{n-1}", + "\\frac{d(\\sin(x))}{dx} = \\cos(x)", + ])) + known_formulas.arrange_submobjects( + DOWN, buff = MED_LARGE_BUFF, + ) + known_formulas.scale_to_fit_height(2.5) + exp_question = TexMobject("2^x", ", 7^x", ", e^x", " ???") + + last_video_brace = Brace(last_video) + known_formulas.next_to(last_video_brace, DOWN) + last_video_brace.save_state() + last_video_brace.shift(3*LEFT) + last_video_brace.set_fill(opacity = 0) + self.add(series) self.play( - FadeIn(brace), - last_video.highlight, YELLOW + last_video_brace.restore, + last_video.highlight, YELLOW, + self.get_teacher().change_mode, "raise_right_hand", ) - 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.dither() - self.dither(2) - self.play(simple_rules.replace, last_video) - self.play( - last_video.restore, - Animation(simple_rules), - brace.next_to, this_video, DOWN, - this_video.highlight, YELLOW - ) - - #Combination rules - self.play( - Write(combination_rules), - *[ - ApplyMethod(pi.change_mode, "confused") - for pi in self.get_students() - ] - ) - self.dither(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.highlight, YELLOW - ] - self.play( - interior.highlight, YELLOW, - *added_anims, - submobject_mode = "lagged_start" - ) - self.dither() - self.dither() - - #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, SPACE_HEIGHT*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.dither() - answer[1].highlight(GREEN) - self.play( - Transform(question, answer), - self.get_teacher().change_mode, "hooray", - self.get_students()[2].change_mode, "plain", - ) - self.dither() - 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.dither() - self.change_student_modes(*["happy"]*3) - words = map(TextMobject, [ - "composition", "product", - "composition", "sum", - "composition" - ]) - - for word, part in zip(words, reversed(parts)): - word.highlight(YELLOW) - word.next_to(monster, UP) - self.play( - FadeIn(word), - part.scale_in_place, 1.2, - part.highlight, YELLOW - ) - self.dither() - self.play(*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.dither(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.highlight(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.dither() - -class ComingUp(Scene): - def construct(self): - rect = Rectangle(height = 9, width = 16) - rect.set_stroke(WHITE) - rect.scale_to_fit_height(2*SPACE_HEIGHT-2) - title = TextMobject("Coming up...") - title.to_edge(UP) - rect.next_to(title, DOWN) - - self.play(Write(title)) - self.play(ShowCreation(rect)) - self.dither() - -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].highlight(SINE_COLOR) - formula[6].highlight(SINE_COLOR) - formula[3].highlight(X_SQUARED_COLOR) - formula[8].highlight(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.dither(2) - for indices in [(1, 2, 3), (6,), (7, 8)]: - self.play(*[ - ReplacementTransform( - specific[i].copy(), general[i] - ) - for i in indices - ]) - self.dither() - - #Highlight parts - for i in 1, 3, -1, 6, 8: - if i < 0: - self.dither() - else: - part = specific[i] - self.play( - part.highlight, YELLOW, - part.scale_in_place, 1.2, - rate_func = there_and_back - ) - self.dither() - -class SumRule(GraphScene): - CONFIG = { - "x_labeled_nums" : [], - "y_labeled_nums" : [], - "y_axis_label" : "", - "x_max" : 4, - "x_axis_width" : 2*SPACE_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.highlight_by_tex("f(x)", SUM_COLOR) - func_mob.highlight_by_tex("\\sin(x)", SINE_COLOR) - func_mob.highlight_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.dither() - - 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.dither() - 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.dither() - - self.play(ShowCreation(sum_graph)) - self.dither() - - 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(*map(MoveToTarget, [ - graph_parts, self.graph_labels - ])) - self.dither() - - 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(*filter( - lambda line : line not in example_v_lines, - it.chain(*v_line_sets) - )) - 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.highlight(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.dither() - self.play( - GrowFromCenter(sine_lines[0].brace), - Write(sine_lines[0].brace_text), - ) - self.dither() - self.play( - sine_lines[0].shift, UP+4*LEFT, - sine_lines[0].brace_anim, - parabola_lines[0].move_to, sine_lines[0], DOWN - ) - self.dither() - parabola_lines[0].brace_anim.update(1) - self.play( - GrowFromCenter(parabola_lines[0].brace), - Write(parabola_lines[0].brace_text), - ) - self.dither() - 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.dither() - for line in nudged_v_lines: - self.play( - GrowFromCenter(line.brace), - Write(line.brace_text) - ) - self.dither() - - 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.highlight(SUM_COLOR) - d_sine.highlight(SINE_COLOR) - d_x_squared.highlight(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.dither() - 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.dither() - - 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.dither() - 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.highlight_by_tex("df", SUM_COLOR) - VGroup(*expanded_deriv[2:4]).highlight(SINE_COLOR) - VGroup(*expanded_deriv[5:7]).highlight(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].highlight(SUM_COLOR) - rearranged_deriv[3].highlight(SINE_COLOR) - rearranged_deriv[5].highlight(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.dither() - - 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.highlight, 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.highlight(old_label.get_color()) - - self.play(FocusOn(old_label)) - indicate(old_label) - self.dither() - self.play(FadeOut(old_label)) - self.play(FadeIn(new_label)) - self.dither() - indicate(dx) - self.dither() - indicate(pre_dx) - self.dither() - self.dither() - self.play(*[ - Transform( - expanded_deriv[i], rearranged_deriv[j], - path_arc = -np.pi/2 - ) - for i, j in enumerate(deriv_target_indices) - ]) - self.dither() - -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].highlight(SINE_COLOR) - wrong_product_rule[3].highlight(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.dither(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.highlight, RED - ] - ) - self.dither() - 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.dither(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.dither() - - everything = VGroup(*filter( - lambda m : not m.is_subpath, - self.get_mobjects() - )) - words = TextMobject("Not the best visualization") - words.scale(1.5) - words.shift(SPACE_HEIGHT*UP/2) - words.add_background_rectangle() - words.highlight(RED) - self.play( - everything.fade, - Write(words) - ) - self.dither() - -class ConfusedMorty(Scene): - def construct(self): - morty = Mortimer() - self.add(morty) - self.dither() - self.play(morty.change_mode, "confused") - self.play(Blink(morty)) - self.dither(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.dither() - for mob in self.x_slider: - self.play(Write(mob, run_time = 1)) - self.dither() - for new_x in 0.5, 2, self.default_x: - self.animate_x_change( - new_x, run_time = 2 - ) - self.dither() - - 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.highlight(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.scale_to_fit_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.dither() - self.play(*it.chain( - map(FadeOut, [y_axis, graph, label, v_line]), - [Animation(x_axis)] - )) - self.dither() - for x in 1, 0.5, self.default_x: - self.animate_x_change(x) - self.dither() - - 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].highlight(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.dither() - - 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(*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.dither() - 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.dither() - - 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.highlight(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.highlight(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.highlight(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.dither() - - self.highlight_right_boxes() - self.highlight_bottom_boxes() - self.describe_bottom_box(bottom_box_area) - self.describe_right_box(right_box_area) - self.ignore_corner() - - # self.add(deriv) - - def highlight_boxes_and_label(self, boxes, label): - boxes.save_state() - label.save_state() - - self.play(GrowFromCenter(label)) - self.play( - boxes.highlight, RED, - label.highlight, RED, - ) - self.play( - label[1].scale_in_place, 1.1, - rate_func = there_and_back - ) - self.play(boxes.restore, label.restore) - self.dither() - - def highlight_right_boxes(self): - self.highlight_boxes_and_label( - VGroup(*self.df_boxes[1:]), - self.df_box_labels[0] - ) - - def highlight_bottom_boxes(self): - self.highlight_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(*filter( - lambda m : m not in [bottom_box, top_label, right_label], - self.full_box_parts - )) - faders.save_state() - - self.play(faders.fade, 0.8) - self.dither() - 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.dither() - 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(*filter( - lambda m : m not in [right_box, top_label, right_label], - self.full_box_parts - )) - faders.save_state() - - self.play(faders.fade, 0.8) - self.dither() - 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.dither() - 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.highlight(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.highlight, RED, - corner_copy.highlight, RED, - ) - self.dither() - self.play(Write(words), ShowCreation(arrow)) - self.dither() - self.play(*map(FadeOut, [words, arrow, corner_copy])) - self.dither() - corner_copy.highlight(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.dither() - - 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.highlight(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.highlight, YELLOW, - rate_func = there_and_back - ) - - - for index in 6, 3: - self.deriv.submobjects.insert( - index+1, self.deriv[index].copy() - ) - non_deriv_indices = 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.dither() - indicate(top_part) - self.dither() - self.play(ReplacementTransform(top_part, bottom_part)) - self.dither() - 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.dither() - 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.dither() - for index in 0, 1, 3, 4, 6, 7: - indicate(final_deriv[index]) - self.dither() - - 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.dither() - - 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]).highlight(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.dither() - - ############### - - 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 = range(int(self.slider_x_max) + 1) - line = NumberLine( - x_min = 0, - x_max = self.slider_x_max, - space_unit_to_num = 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.scale_to_fit_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.highlight(RED) - self.play(Write(term)) - self.dither() - -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]).highlight(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.dither() - self.play(*map(Indicate, [left, left_words[0], left_terms[0]])) - self.dither() - self.play(*map(Indicate, [right, left_words[1], left_terms[1]])) - self.dither() - - 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.dither() - self.play(*map(Indicate, [right, right_words[1], right_terms[1]])) - self.dither() - self.play(*map(Indicate, [left, right_words[2], right_terms[2]])) - self.dither(3) - - self.play(self.get_teacher().change_mode, "shruggie") - self.dither() - self.change_student_modes(*["confused"]*3) - self.dither(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.play(Write(known_formulas)) self.dither() 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.dither(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.highlight(SINE_COLOR) - x_squared = TexMobject("h(x)", "=", "x^2") - x_squared.highlight(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].highlight(SINE_COLOR) - for i in 1, 5: - composition[i].highlight(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.dither() - 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.dither() - self.dither() - self.play( - GrowFromCenter(brace), - Write(deriv_q) - ) - self.dither() - -class ThreeLinesChainRule(ReconfigurableScene): - CONFIG = { - "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" : 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" : 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.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) - ]) - 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) + exp_question, student_index = -1, + added_anims = [self.get_teacher().change_mode, "pondering"] ) 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 + self.play(known_formulas.replace, last_video) + self.play(last_video_brace.next_to, this_video, DOWN) + self.play( + last_video.restore, + this_video.highlight, YELLOW ) - dx_squared_value.shift( - dx_squared_brace.text.get_right()+MED_SMALL_BUFF*RIGHT - \ - dx_squared_value[0].get_left() + self.play( + exp_question.next_to, last_video_brace, DOWN, + FadeOut(self.get_students()[-1].bubble), ) - dsine_value = TextMobject( - "$=\\cos(%s)$"%self.line_configs[1]["func_label"], - dx_squared_brace.text.get_tex_string() + self.change_student_modes( + *["pondering"]*3, + look_at_arg = exp_question ) - dsine_value.next_to(dsine_brace.text) - less_than_zero = TexMobject("<0") - less_than_zero.next_to(dsine_brace.text) + self.dither() - all_x_squared_relevant_labels = VGroup( - dx_squared_brace, dsine_brace, - labels[1], labels[2], - dsine_value, - ) - all_x_squared_relevant_labels.save_state() +class PopulationSizeGraphVsPopulationMassGraph(Scene): + def construct(self): + pass - 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)) +class DoublingPopulation(PiCreatureScene): + CONFIG = { + "time_color" : YELLOW, + "pi_creature_grid_dimensions" : (8, 8), + "pi_creature_grid_height" : 6, + } + + def construct(self): + self.remove(self.get_pi_creatures()) + self.introduce_expression() + self.introduce_pi_creatures() + self.count_through_days() + self.ask_about_dM_dt() + self.growth_per_day() + self.relate_growth_rate_to_pop_size() + + def introduce_expression(self): + f_x = TexMobject("f(x)", "=", "2^x") + f_t = TexMobject("f(t)", "=", "2^t") + P_t = TexMobject("P(t)", "=", "2^t") + M_t = TexMobject("M(t)", "=", "2^t") + functions = VGroup(f_x, f_t, P_t, M_t) + for function in functions: + function.scale(1.2) + function.to_corner(UP+LEFT) + for function in functions[1:]: + for i, j in (0, 2), (2, 1): + function[i][j].highlight(self.time_color) + + t_expression = TexMobject("t", "=", "\\text{Time (in days)}") + t_expression.to_corner(UP+RIGHT) + t_expression[0].highlight(self.time_color) + + pop_brace, mass_brace = [ + Brace(function[0], DOWN) + for function in P_t, M_t + ] + for brace, word in (pop_brace, "size"), (mass_brace, "mass"): + text = brace.get_text("Population %s"%word) + text.to_edge(LEFT) + brace.text = text + + self.play(Write(f_x)) self.dither() self.play( - all_x_squared_relevant_labels.restore, - submobject_mode = "lagged_start", - lag_factor = 3, - run_time = 3, + Transform(f_x, f_t), + FadeIn( + t_expression, + run_time = 2, + submobject_mode = "lagged_start" + ) ) - self.__dict__.update(self.__class__.CONFIG) + self.play(Transform(f_x, P_t)) + self.play( + GrowFromCenter(pop_brace), + Write(pop_brace.text, run_time = 2) + ) + self.dither(2) + + self.function = f_x + self.pop_brace = pop_brace + self.t_expression = t_expression + self.mass_function = M_t + self.mass_brace = mass_brace + + def introduce_pi_creatures(self): + creatures = self.get_pi_creatures() + total_num_days = self.get_num_days() + num_start_days = 4 + + self.reset() + for x in range(num_start_days): + self.let_one_day_pass() self.dither() - for mob in dsine_value: + self.play( + Transform(self.function, self.mass_function), + Transform(self.pop_brace, self.mass_brace), + Transform(self.pop_brace.text, self.mass_brace.text), + ) + self.dither() + for x in range(total_num_days-num_start_days): + self.let_one_day_pass() + self.dither() + self.joint_blink(shuffle = False) + self.dither() + + def count_through_days(self): + self.reset() + brace = self.get_population_size_descriptor() + days_to_let_pass = 3 + + self.play(GrowFromCenter(brace)) + self.dither() + for x in range(days_to_let_pass): + self.let_one_day_pass() + new_brace = self.get_population_size_descriptor() + self.play(Transform(brace, new_brace)) + self.dither() + + self.population_size_descriptor = brace + + def ask_about_dM_dt(self): + dM_dt_question = TexMobject("{dM", "\\over dt}", "=", "???") + dM, dt, equals, q_marks = dM_dt_question + dM_dt_question.next_to(self.function, DOWN, buff = LARGE_BUFF) + dM_dt_question.to_edge(LEFT) + + self.play( + FadeOut(self.pop_brace), + FadeOut(self.pop_brace.text), + Write(dM_dt_question) + ) + self.dither(3) + for mob in dM, dt: self.play(Indicate(mob)) 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(FocusOn(two_x_dx)) - self.play(Write(two_x_dx)) + self.dM_dt_question = dM_dt_question + + def growth_per_day(self): + day_to_day, frac = self.get_from_day_to_day_label() + self.play( - two_x_dx_copy.move_to, dx_squared, LEFT, - dx_squared.next_to, dx_squared, UP, - run_time = 2 + FadeOut(self.dM_dt_question), + FadeOut(self.population_size_descriptor), + FadeIn(day_to_day) ) - self.play(FadeOut(dx_squared)) - for sublist in two_x_dx_copy[:2], two_x_dx_copy[2:]: - self.play(Indicate(VGroup(*sublist))) - self.dither() - self.dither(2) + rect = self.let_day_pass_and_highlight_new_creatures(frac) - self.final_derivative = dsine_value + for x in range(2): + new_day_to_day, new_frac = self.get_from_day_to_day_label() + self.play(*map(FadeOut, [rect, frac])) + frac = new_frac + self.play(Transform(day_to_day, new_day_to_day)) + rect = self.let_day_pass_and_highlight_new_creatures(frac) + self.play(*map(FadeOut, [rect, frac, day_to_day])) - def discuss_dsine_sign(self, less_than_zero): - self.wiggle_by_dx() + def let_day_pass_and_highlight_new_creatures(self, frac): + num_new_creatures = 2**self.get_curr_day() + + self.let_one_day_pass() + new_creatures = VGroup( + *self.get_on_screen_pi_creatures()[-num_new_creatures:] + ) + rect = Rectangle( + color = GREEN, + fill_color = BLUE, + fill_opacity = 0.3, + ) + rect.replace(new_creatures, stretch = True) + rect.stretch_to_fit_height(rect.get_height()+MED_SMALL_BUFF) + rect.stretch_to_fit_width(rect.get_width()+MED_SMALL_BUFF) + self.play(DrawBorderThenFill(rect)) + self.play(Write(frac)) 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)) + return rect - 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 relate_growth_rate_to_pop_size(self): + false_deriv = TexMobject( + "{d(2^t) ", "\\over dt}", "= 2^t" ) - - 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 + difference_eq = TexMobject( + "{ {2^{t+1} - 2^t} \\over", "1}", "= 2^t" ) - - 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 + real_deriv = TexMobject( + "{ {2^{t+dt} - 2^t} \\over", "dt}", "= \\, ???" ) + VGroup( + false_deriv[0][3], + false_deriv[2][-1], + difference_eq[0][1], + difference_eq[0][-2], + difference_eq[2][-1], + difference_eq[2][-1], + real_deriv[0][1], + real_deriv[0][-2], + ).highlight(YELLOW) + VGroup( + difference_eq[0][3], + difference_eq[1][-1], + real_deriv[0][3], + real_deriv[0][4], + real_deriv[1][-2], + real_deriv[1][-1], + ).highlight(GREEN) - 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.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: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.dither() - self.dither() - 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 - self.play(*map(FadeIn, general[:5])) - self.dither() - 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.dither() - 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.dither(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.dither(2) - - #Name chain rule - name = TextMobject("``Chain rule''") - name.scale(1.2) - name.highlight(YELLOW) - name.to_corner(UP+LEFT) - self.play(Write(name)) - self.dither() - - #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.dither() - 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.dither() - 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.dither() - self.play(morty.change_mode, "pondering") - self.play(Blink(morty)) - self.dither() - self.play(*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, + expressions = [false_deriv, difference_eq, real_deriv] + text_arg_list = [ + ("Tempting", "...",), + ("Rate of change", "\\\\ over one full day"), + ("Rate of change", "\\\\ in a small time"), ] - texts = [brace.text for brace in braces] - self.play(*map(FadeOut, braces+texts)) + for expression, text_args in zip(expressions, text_arg_list): + expression.next_to( + self.function, DOWN, + buff = LARGE_BUFF, + aligned_edge = LEFT, + ) + expression.brace = Brace(expression, DOWN) + expression.brace_text = expression.brace.get_text(*text_args) - 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(*map(VectorizedPoint, - [m.get_left() for m in to_collapse] - )) + time = self.t_expression[-1] + new_time = TexMobject("3") + new_time.move_to(time, LEFT) + + fading_creatures = VGroup(*self.get_on_screen_pi_creatures()[8:]) + + + self.play(*map(FadeIn, [ + false_deriv, false_deriv.brace, false_deriv.brace_text + ])) + self.dither() self.play( - Transform(to_collapse, points), - dh_dx.next_to, dg_dh, - morty.look_at, dg_dh, + Transform(time, new_time), + FadeOut(fading_creatures) ) self.dither() - 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.dither() - self.play(FadeOut(circle)) + for x in range(3): + self.let_one_day_pass(run_time = 2) + self.dither(2) - 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.highlight(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.dither(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.dither() - self.play(Blink(morty)) - self.dither() - -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.dither() - self.play(randy.change_mode, "erm") - self.play(Blink(randy)) - self.dither() - self.play(randy.change_mode, "maybe") - self.dither() - self.play(Blink(randy)) - for formula in formulas: + for expression in difference_eq, real_deriv: + expression.brace_text[1].highlight(GREEN) self.play( - Write(formula, run_time = 2), - randy.change_mode, "thinking" + Transform(false_deriv, expression), + Transform(false_deriv.brace, expression.brace), + Transform(false_deriv.brace_text, expression.brace_text), + ) + self.dither(3) + self.reset() + for x in range(self.get_num_days()): + self.let_one_day_pass() + self.dither() + + rect = Rectangle(color = YELLOW) + rect.replace(real_deriv) + rect.stretch_to_fit_width(rect.get_width()+MED_SMALL_BUFF) + rect.stretch_to_fit_height(rect.get_height()+MED_SMALL_BUFF) + self.play(*map(FadeOut, [ + false_deriv.brace, false_deriv.brace_text + ])) + self.play(ShowCreation(rect)) + self.play(*[ + ApplyFunction( + lambda pi : pi.change_mode("pondering").look_at(real_deriv), + pi, + run_time = 2, + rate_func = squish_rate_func(smooth, a, a+0.5) + ) + for pi in self.get_pi_creatures() + for a in [0.5*random.random()] + ]) + self.dither(3) + + ########### + + def create_pi_creatures(self): + width, height = self.pi_creature_grid_dimensions + creature_array = VGroup(*[ + VGroup(*[ + PiCreature(mode = "plain") + for y in range(height) + ]).arrange_submobjects(UP, buff = MED_LARGE_BUFF) + for x in range(width) + ]).arrange_submobjects(RIGHT, buff = MED_LARGE_BUFF) + creatures = VGroup(*it.chain(*creature_array)) + creatures.scale_to_fit_height(self.pi_creature_grid_height) + creatures.to_corner(DOWN+RIGHT) + + colors = color_gradient([BLUE, GREEN, GREY_BROWN], len(creatures)) + random.shuffle(colors) + for creature, color in zip(creatures, colors): + creature.set_color(color) + + return creatures + + def reset(self): + time = self.t_expression[-1] + faders = [time] + list(self.get_on_screen_pi_creatures()) + new_time = TexMobject("0") + new_time.next_to(self.t_expression[-2], RIGHT) + first_creature = self.get_pi_creatures()[0] + + self.play(*map(FadeOut, faders)) + self.play(*map(FadeIn, [first_creature, new_time])) + self.t_expression.submobjects[-1] = new_time + + def let_one_day_pass(self, run_time = 2): + all_creatures = self.get_pi_creatures() + on_screen_creatures = self.get_on_screen_pi_creatures() + low_i = len(on_screen_creatures) + high_i = min(2*low_i, len(all_creatures)) + new_creatures = VGroup(*all_creatures[low_i:high_i]) + + to_children_anims = [] + growing_anims = [] + for old_pi, pi in zip(on_screen_creatures, new_creatures): + pi.save_state() + child = pi.copy() + child.scale(0.25, about_point = child.get_bottom()) + child.eyes.scale(1.5, about_point = child.eyes.get_bottom()) + pi.move_to(old_pi) + pi.set_fill(opacity = 0) + + index = list(new_creatures).index(pi) + prop = float(index)/len(new_creatures) + alpha = np.clip(len(new_creatures)/8.0, 0, 0.5) + rate_func = squish_rate_func( + smooth, alpha*prop, alpha*prop+(1-alpha) + ) + + to_child_anim = Transform(pi, child, rate_func = rate_func) + to_child_anim.update(1) + growing_anim = ApplyMethod(pi.restore, rate_func = rate_func) + to_child_anim.update(0) + + to_children_anims.append(to_child_anim) + growing_anims.append(growing_anim) + + time = self.t_expression[-1] + total_new_creatures = len(on_screen_creatures) + len(new_creatures) + new_time = TexMobject(str(int(np.log2(total_new_creatures)))) + new_time.move_to(time, LEFT) + + growing_anims.append(Transform(time, new_time)) + + self.play(*to_children_anims, run_time = run_time/2.0) + self.play(*growing_anims, run_time = run_time/2.0) + + def get_num_pi_creatures_on_screen(self): + mobjects = self.get_mobjects() + return sum([ + pi in mobjects for pi in self.get_pi_creatures() + ]) + + def get_population_size_descriptor(self): + on_screen_creatures = self.get_on_screen_pi_creatures() + brace = Brace(on_screen_creatures, LEFT) + n = len(on_screen_creatures) + label = brace.get_text( + "$2^%d$"%int(np.log2(n)), + "$=%d$"%n, + ) + brace.add(label) + return brace + + def get_num_days(self): + x, y = self.pi_creature_grid_dimensions + return int(np.log2(x*y)) + + def get_curr_day(self): + return int(np.log2(len(self.get_on_screen_pi_creatures()))) + + def get_from_day_to_day_label(self): + curr_day = self.get_curr_day() + top_words = TextMobject( + "From day", str(curr_day), + "to", str(curr_day+1), ":" + ) + top_words.scale_to_fit_width(4) + top_words.next_to( + self.function, DOWN, + buff = MED_LARGE_BUFF, + aligned_edge = LEFT, + ) + top_words[1].highlight(GREEN) + + bottom_words = TexMobject( + str(2**curr_day), + "\\text{ creatures}", "\\over {1 \\text{ day}}" + ) + bottom_words[0].highlight(GREEN) + bottom_words.next_to(top_words, DOWN, buff = MED_LARGE_BUFF) + + return top_words, bottom_words + +class GraphOfTwoToT(GraphScene): + CONFIG = { + "x_axis_label" : "$t$", + "y_axis_label" : "$M$", + "x_labeled_nums" : range(1, 7), + "y_labeled_nums" : range(8, 40, 8), + "x_max" : 6, + "y_min" : 0, + "y_max" : 32, + "y_tick_frequency" : 2, + "graph_origin" : 2.5*DOWN + 5*LEFT, + } + def construct(self): + self.setup_axes() + example_t = 3 + graph = self.get_graph(lambda t : 2**t, color = BLUE_C) + self.graph = graph + graph_label = self.get_graph_label( + graph, "M(t) = 2^t", + direction = LEFT, + ) + label_group = self.get_label_group(example_t) + v_line, brace, height_label, ss_group, slope_label = label_group + self.animate_secant_slope_group_change( + ss_group, + target_dx = 1, + run_time = 0 + ) + self.remove(ss_group) + + #Draw graph and revert to tangent + self.play(ShowCreation(graph)) + self.play(Write(graph_label)) + self.dither() + self.play(Write(ss_group)) + self.dither() + for target_dx in 0.01, 1, 0.01: + self.animate_secant_slope_group_change( + ss_group, + target_dx = target_dx ) self.dither() - def create_pi_creatures(self): - return [Randolph().shift(DOWN+RIGHT)] + #Mark up with values + + self.play(ShowCreation(v_line)) + self.play( + GrowFromCenter(brace), + Write(height_label, run_time = 1) + ) + self.dither() + self.play( + FadeIn( + slope_label, + run_time = 4, + submobject_mode = "lagged_start" + ), + ReplacementTransform( + height_label.copy(), + slope_label.get_part_by_tex("2^") + ) + ) + self.dither() + + #Vary value + threes = VGroup(height_label[1], slope_label[2][1]) + ts = VGroup(*[ + TexMobject("t").highlight(YELLOW).scale(0.75).move_to(three) + for three in threes + ]) + self.play(Transform(threes, ts)) + + alt_example_t = example_t+1 + def update_label_group(group, alpha): + t = interpolate(example_t, alt_example_t, alpha) + new_group = self.get_label_group(t) + Transform(group, new_group).update(1) + for t, three in zip(ts, threes): + t.move_to(three) + Transform(threes, ts).update(1) + return group + + self.play(UpdateFromAlphaFunc( + label_group, update_label_group, + run_time = 3, + )) + self.play(UpdateFromAlphaFunc( + label_group, update_label_group, + run_time = 3, + rate_func = lambda t : 1 - 1.5*smooth(t) + )) + + def get_label_group(self, t): + graph = self.graph + + v_line = self.get_vertical_line_to_graph( + t, graph, + color = YELLOW, + ) + brace = Brace(v_line, RIGHT) + height_label = brace.get_text("$2^%d$"%t) + + ss_group = self.get_secant_slope_group( + t, graph, dx = 0.01, + df_label = "dM", + dx_label = "dt", + dx_line_color = GREEN, + secant_line_color = RED, + ) + slope_label = TexMobject( + "\\text{Slope}", "=", + "2^%d"%t, + "(%.7f\\dots)"%np.log(2) + ) + slope_label.next_to( + ss_group.secant_line.point_from_proportion(0.65), + DOWN+RIGHT, + buff = 0 + ) + slope_label.highlight_by_tex("Slope", RED) + return VGroup( + v_line, brace, height_label, + ss_group, slope_label + ) + +class SimpleGraphOfTwoToT(GraphOfTwoToT): + CONFIG = { + "x_axis_label" : "", + "y_axis_label" : "", + } + def construct(self): + self.setup_axes() + func = lambda t : 2**t + graph = self.get_graph(func) + line_pairs = VGroup() + for x in 1, 2, 3, 4, 5: + point = self.coords_to_point(x, func(x)) + x_axis_point = self.coords_to_point(x, 0) + y_axis_point = self.coords_to_point(0, func(x)) + line_pairs.add(VGroup( + DashedLine(x_axis_point, point), + DashedLine(y_axis_point, point), + )) + + + self.play(ShowCreation(graph, run_time = 2)) + for pair in line_pairs: + self.play(ShowCreation(pair)) + self.dither() + +class AnalyzeExponentRatio(PiCreatureScene): + CONFIG = { + "base" : 2, + "base_str" : "2", + } + def construct(self): + base_str = self.base_str + + func_def = TexMobject("M(", "t", ")", "= ", "%s^"%base_str, "t") + func_def.to_corner(UP+LEFT) + self.add(func_def) + + ratio = TexMobject( + "{ {%s^"%base_str, "{t", "+", "dt}", "-", + "%s^"%base_str, "t}", + "\\over \\,", "dt}" + ) + ratio.shift(UP+LEFT) + + lhs = TexMobject("{dM", "\\over \\,", "dt}", "(", "t", ")", "=") + lhs.next_to(ratio, LEFT) + + + two_to_t_plus_dt = VGroup(*ratio[:4]) + two_to_t = VGroup(*ratio[5:7]) + two_to_t_two_to_dt = TexMobject( + "%s^"%base_str, "t", + "%s^"%base_str, "{dt}" + ) + two_to_t_two_to_dt.move_to(two_to_t_plus_dt, DOWN+LEFT) + exp_prop_brace = Brace(two_to_t_two_to_dt, UP) + + one = TexMobject("1") + one.move_to(ratio[5], DOWN) + lp, rp = parens = TexMobject("()") + parens.stretch(1.3, 1) + parens.scale_to_fit_height(ratio.get_height()) + lp.next_to(ratio, LEFT, buff = 0) + rp.next_to(ratio, RIGHT, buff = 0) + + extracted_two_to_t = TexMobject("%s^"%base_str, "t") + extracted_two_to_t.next_to(lp, LEFT, buff = SMALL_BUFF) + + expressions = [ + ratio, two_to_t_two_to_dt, + extracted_two_to_t, lhs, func_def + ] + for expression in expressions: + expression.highlight_by_tex("t", YELLOW) + expression.highlight_by_tex("dt", GREEN) + + #Apply exponential property + self.play( + Write(ratio), Write(lhs), + self.pi_creature.change_mode, "raise_right_hand" + ) + self.dither(2) + self.play( + two_to_t_plus_dt.next_to, exp_prop_brace, UP, + self.pi_creature.change_mode, "pondering" + ) + self.play( + ReplacementTransform( + two_to_t_plus_dt.copy(), two_to_t_two_to_dt, + run_time = 2, + path_arc = np.pi, + ), + FadeIn(exp_prop_brace) + ) + self.dither(2) + + #Talk about exponential property + add_exp_rect, mult_rect = rects = [ + Rectangle( + stroke_color = BLUE, + stroke_width = 2, + ).replace(mob).scale_in_place(1.1) + for mob in [ + VGroup(*two_to_t_plus_dt[1:]), + two_to_t_two_to_dt + ] + ] + words = VGroup(*[ + TextMobject(s, " ideas") + for s in "Additive", "Multiplicative" + ]) + words[0].move_to(words[1], LEFT) + words.highlight(BLUE) + words.next_to(two_to_t_plus_dt, RIGHT, buff = 1.5*LARGE_BUFF) + arrows = VGroup(*[ + Arrow(word.get_left(), rect, color = words.get_color()) + for word, rect in zip(words, rects) + ]) + + self.play(ShowCreation(add_exp_rect)) + self.dither() + self.play(ReplacementTransform( + add_exp_rect.copy(), mult_rect + )) + self.dither() + self.change_mode("happy") + self.play(Write(words[0], run_time = 2)) + self.play(ShowCreation(arrows[0])) + self.dither() + self.play( + Transform(*words), + Transform(*arrows), + ) + self.dither(2) + self.play(*map(FadeOut, [ + words[0], arrows[0], add_exp_rect, mult_rect, + two_to_t_plus_dt, exp_prop_brace, + ])) + + #Factor out 2^t + self.play(*[ + FadeIn( + mob, + run_time = 2, + rate_func = squish_rate_func(smooth, 0.5, 1) + ) + for mob in one, lp, rp + ] + [ + ReplacementTransform( + mob, extracted_two_to_t, + path_arc = np.pi/2, + run_time = 2, + ) + for mob in two_to_t, VGroup(*two_to_t_two_to_dt[:2]) + ] + [ + lhs.next_to, extracted_two_to_t, LEFT + ]) + self.change_mode("pondering") + shifter = VGroup(ratio[4], one, *two_to_t_two_to_dt[2:]) + stretcher = VGroup(lp, ratio[7], rp) + self.play( + shifter.next_to, ratio[7], UP, + stretcher.stretch_in_place, 0.9, 0 + ) + self.dither(2) + + #Ask about dt -> 0 + brace = Brace(VGroup(extracted_two_to_t, ratio), DOWN) + alt_brace = Brace(parens, DOWN) + dt_to_zero = TexMobject("dt", "\\to 0") + dt_to_zero.highlight_by_tex("dt", GREEN) + dt_to_zero.next_to(brace, DOWN) + + self.play(GrowFromCenter(brace)) + self.play(Write(dt_to_zero)) + self.dither(2) + + #Who cares + randy = Randolph() + randy.scale(0.7) + randy.to_edge(DOWN) + + self.play( + FadeIn(randy), + self.pi_creature.change_mode, "plain", + ) + self.play(PiCreatureSays( + randy, "Who cares?", + bubble_kwargs = {"direction" : LEFT}, + target_mode = "angry", + )) + self.dither(2) + self.play( + RemovePiCreatureBubble(randy), + FadeOut(randy), + self.pi_creature.change_mode, "hooray", + self.pi_creature.look_at, parens + ) + self.play( + Transform(brace, alt_brace), + dt_to_zero.next_to, alt_brace, DOWN + ) + self.dither() + + #Highlight separation + rects = [ + Rectangle( + stroke_color = color, + stroke_width = 2, + ).replace(mob, stretch = True).scale_in_place(1.1) + for mob, color in [ + (VGroup(parens, dt_to_zero), GREEN), + (extracted_two_to_t, YELLOW), + ] + ] + self.play(ShowCreation(rects[0])) + self.dither(2) + self.play(ReplacementTransform(rects[0].copy(), rects[1])) + self.change_mode("happy") + self.dither() + self.play(*map(FadeOut, rects)) + + #Plug in specific values + static_constant = self.try_specific_dt_values() + constant = static_constant.copy() + + #Replace with actual constant + limit_term = VGroup( + brace, dt_to_zero, ratio[4], one, rects[0], + *ratio[7:]+two_to_t_two_to_dt[2:] + ) + self.play(FadeIn(rects[0])) + self.play(limit_term.to_corner, DOWN+LEFT) + self.play( + lp.stretch, 0.5, 1, + lp.stretch, 0.8, 0, + lp.next_to, extracted_two_to_t[0], RIGHT, + rp.stretch, 0.5, 1, + rp.stretch, 0.8, 0, + rp.next_to, lp, RIGHT, SMALL_BUFF, + rp.shift, constant.get_width()*RIGHT, + constant.next_to, extracted_two_to_t[0], RIGHT, MED_LARGE_BUFF + ) + self.dither() + self.change_mode("confused") + self.dither() + + #Indicate distinction between dt group and t group again + for mob in limit_term, extracted_two_to_t: + self.play(FocusOn(mob)) + self.play(Indicate(mob)) + self.dither() + + #hold_final_value + derivative = VGroup( + lhs, extracted_two_to_t, parens, constant + ) + func_def_rhs = VGroup(*func_def[-2:]).copy() + func_lp, func_rp = func_parens = TexMobject("()") + func_parens.set_fill(opacity = 0) + func_lp.next_to(func_def_rhs[0], LEFT, buff = 0) + func_rp.next_to(func_lp, RIGHT, buff = func_def_rhs.get_width()) + func_def_rhs.add(func_parens) + M = lhs[0][1] + + self.play( + FadeOut(M), + func_def_rhs.move_to, M, LEFT, + func_def_rhs.set_fill, None, 1, + ) + lhs[0].submobjects[1] = func_def_rhs + self.dither() + self.play( + derivative.next_to, self.pi_creature, UP, + derivative.to_edge, RIGHT, + self.pi_creature.change_mode, "raise_right_hand" + ) + self.dither(2) + for mob in extracted_two_to_t, constant: + self.play(Indicate(mob)) + self.dither() + self.dither(2) + + def try_specific_dt_values(self): + expressions = [] + for num_zeros in [1, 2, 4, 7]: + dt_str = "0." + num_zeros*"0" + "1" + dt_num = float(dt_str) + output_num = (self.base**dt_num - 1) / dt_num + output_str = "%.7f\\dots"%output_num + + expression = TexMobject( + "{%s^"%self.base_str, "{%s}"%dt_str, "-1", + "\\over \\,", "%s}"%dt_str, + "=", output_str + ) + expression.highlight_by_tex(dt_str, GREEN) + expression.highlight_by_tex(output_str, BLUE) + expression.to_corner(UP+RIGHT) + expressions.append(expression) + + curr_expression = expressions[0] + self.play( + Write(curr_expression), + self.pi_creature.change_mode, "pondering" + ) + self.dither(2) + for expression in expressions[1:]: + self.play(Transform(curr_expression, expression)) + self.dither(2) + return curr_expression[-1] + +class ExponentRatioWithThree(AnalyzeExponentRatio): + CONFIG = { + "base" : 3, + "base_str" : "3", + } + +class ExponentRatioWithSeven(AnalyzeExponentRatio): + CONFIG = { + "base" : 7, + "base_str" : "7", + } + +class ExponentRatioWithE(AnalyzeExponentRatio): + CONFIG = { + "base" : np.exp(1), + "base_str" : "e", + } + +class AskAboutConstantOne(TeacherStudentsScene): + def construct(self): + note = TexMobject( + "{ d(a^", "t", ")", "\\over \\,", "dt}", + "=", "a^", "t", "(\\text{Some constant})" + ) + note.highlight_by_tex("t", YELLOW) + note.highlight_by_tex("dt", GREEN) + note.highlight_by_tex("constant", BLUE) + note.to_corner(UP+LEFT) + self.add(note) + + self.student_says( + "Is there a base where\\\\", + "that constant is 1?" + ) + self.change_student_modes( + "pondering", "raise_right_hand", "thinking", + # look_at_arg = self.get_students()[1].bubble + ) + self.dither(2) + self.play(FadeOut(note[-1], run_time = 3)) + self.dither() + + self.teacher_says( + "There is!\\\\", + "$e = 2.71828\\dots$", + target_mode = "hooray" + ) + self.change_student_modes(*["confused"]*3) + self.dither(3) + +class NaturalLog(Scene): + def construct(self): + expressions = VGroup(*map(self.get_expression, [2, 3, 7])) + expressions.arrange_submobjects(DOWN, buff = MED_SMALL_BUFF) + expressions.to_edge(LEFT) + + self.play(FadeIn( + expressions, + run_time = 3, + submobject_mode = "lagged_start" + )) + self.dither() + self.play( + expressions.set_fill, None, 1, + run_time = 2, + submobject_mode = "lagged_start" + ) + self.dither() + for i in 0, 2, 1: + self.show_natural_loggedness(expressions[i]) + + def show_natural_loggedness(self, expression): + base, constant = expression[1], expression[-3] + + log_constant, exp_constant = constant.copy(), constant.copy() + log_base, exp_base = base.copy(), base.copy() + log_equals, exp_equals = map(TexMobject, "==") + + ln = TexMobject("\\ln(2)") + log_base.move_to(ln[-2]) + ln.remove(ln[-2]) + log_equals.next_to(ln, LEFT) + log_constant.next_to(log_equals, LEFT) + log_expression = VGroup( + ln, log_constant, log_equals, log_base + ) + + e = TexMobject("e") + exp_constant.scale(0.7) + exp_constant.next_to(e, UP+RIGHT, buff = 0) + exp_base.next_to(exp_equals, RIGHT) + VGroup(exp_base, exp_equals).next_to( + VGroup(e, exp_constant), RIGHT, + aligned_edge = DOWN + ) + exp_expression = VGroup( + e, exp_constant, exp_equals, exp_base + ) + + for group, vect in (log_expression, UP), (exp_expression, DOWN): + group.to_edge(RIGHT) + group.shift(vect) + + self.play( + ReplacementTransform(base.copy(), log_base), + ReplacementTransform(constant.copy(), log_constant), + run_time = 2 + ) + self.play(Write(ln), Write(log_equals)) + self.dither() + self.play( + ReplacementTransform( + log_expression.copy(), + exp_expression, + run_time = 2, + ) + ) + self.dither(2) + + ln_a = expression[-1] + self.play( + FadeOut(expression[-2]), + FadeOut(constant), + ln_a.move_to, constant, LEFT, + ln_a.highlight, BLUE + ) + self.dither() + self.play(*map(FadeOut, [log_expression, exp_expression])) + self.dither() + + def get_expression(self, base): + expression = TexMobject( + "{d(", "%d^"%base, "t", ")", "\\over \\,", "dt}", + "=", "%d^"%base, "t", "(%.4f\\dots)"%np.log(base), + ) + expression.highlight_by_tex("t", YELLOW) + expression.highlight_by_tex("dt", GREEN) + expression.highlight_by_tex("\\dots", BLUE) + + brace = Brace(expression.get_part_by_tex("\\dots"), UP) + brace_text = brace.get_text("$\\ln(%d)$"%base) + for mob in brace, brace_text: + mob.set_fill(opacity = 0) + + expression.add(brace, brace_text) + return expression class NextVideo(TeacherStudentsScene): def construct(self): series = VideoSeries() series.to_edge(UP) + this_video = series[3] next_video = series[4] + brace = Brace(this_video, DOWN) + this_video.save_state() + this_video.highlight(YELLOW) - pre_expression = TexMobject( - "x", "^2", "+", "y", "^2", "=", "1" + this_tex = TexMobject( + "{d(", "a^t", ") \\over dt} = ", "a^t", "\\ln(a)" ) - d_expression = TexMobject( - "2", "x", "\\,dx", "+", "2", "y", "\\,dy", "=", "0" + this_tex[1][1].highlight(YELLOW) + this_tex[3][1].highlight(YELLOW) + this_tex.next_to(brace, DOWN) + + next_tex = VGroup(*map(TextMobject, [ + "Chain rule", "Product rule", "$\\vdots$" + ])) + next_tex.arrange_submobjects(DOWN) + next_tex.next_to(brace, DOWN) + next_tex.shift( + next_video.get_center()[0]*RIGHT\ + -next_tex.get_center()[0]*RIGHT ) - 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.highlight, YELLOW, - self.get_teacher().change_mode, "raise_right_hand" + self.add(series, brace, *this_tex[:3]) + self.change_student_modes( + "confused", "pondering", "erm", + look_at_arg = this_tex ) + self.play(ReplacementTransform( + this_tex[1].copy(), this_tex[3] + )) self.dither() self.play( - Write(expression), - *[ - ApplyMethod(pi.change_mode, "pondering") - for pi in self.get_students() - ] - ) - self.play(FadeIn(axes)) - self.dither() - self.remove(expression) - self.play(Transform(expression, d_expression, path_arc = np.pi/2)) - self.dither() - self.play( - Rotate( - line, np.pi/4, - about_point = axes.get_center(), - rate_func = wiggle, - run_time = 3 + Write(this_tex[4]), + ReplacementTransform( + this_tex[3][0].copy(), + this_tex[4][3], + path_arc = np.pi, + remover = True ) ) self.dither(2) + self.play(this_tex.replace, this_video) + self.play( + brace.next_to, next_video, DOWN, + this_video.restore, + Animation(this_tex), + next_video.highlight, YELLOW, + Write(next_tex), + self.get_teacher().change_mode, "raise_right_hand" + ) + self.change_student_modes( + *["pondering"]*3, + look_at_arg = next_tex + ) + self.dither(3) -class Chapter4Thanks(PatreonThanks): +class ExpPatreonThanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Ali Yahya", "Meshal Alshammari", "CrypticSwarm ", + "Kathryn Schmiedicke", + "Nathan Pellegrin", + "Karan Bhargava", + "Justin Helps", "Ankit Agarwal", "Yu Jun", - "Shelby Doolittle", "Dave Nicponski", "Damion Kistler", "Juan Benet", @@ -2256,7 +1142,12 @@ class Chapter4Thanks(PatreonThanks): "Derek Dai", "Joseph John Cox", "Luc Ritchie", + "Mustafa Mahdi", + "Daan Smedinga", + "Jonathan Eppele", + "Albert Nguyen", "Nils Schneider", + "Mustafa Mahdi", "Mathew Bramson", "Guido Gambardella", "Jerry Ling", @@ -2271,31 +1162,42 @@ class Chapter4Thanks(PatreonThanks): ] } -class Thumbnail(IntroduceProductAsArea): - CONFIG = { - "default_x" : 0.8, - "dx" : 0.05 - } +class Thumbnail(Scene): 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 + derivative = TexMobject( + "\\frac{d(a^t)}{dt} =", "a^t \\ln(a)" ) - 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.scale_to_fit_height(2*SPACE_HEIGHT-2*MED_LARGE_BUFF) - blg.center() - self.add(blg) - - - - - - + derivative[0][3].highlight(YELLOW) + derivative[1][1].highlight(YELLOW) + derivative[0][2].highlight(BLUE) + derivative[1][0].highlight(BLUE) + derivative[1][5].highlight(BLUE) + derivative.scale(3) + # derivative.to_edge(UP) + derivative.shift(DOWN) + + brace = Brace(Line(LEFT, RIGHT), UP) + brace.scale_to_fit_width(derivative[1].get_width()) + brace.next_to(derivative[1], UP) + question = TextMobject("Why?") + question.scale(2.5) + question.next_to(brace, UP) + + # randy = Randolph() + # randy.scale(1.3) + # randy.next_to(ORIGIN, LEFT).to_edge(DOWN) + # randy.change_mode("pondering") + + # question = TextMobject("What is $e\\,$?") + # e = question[-2] + # e.scale(1.2, about_point = e.get_bottom()) + # e.highlight(BLUE) + # question.scale(1.7) + # question.next_to(randy, RIGHT, aligned_edge = UP) + # question.shift(DOWN) + # randy.look_at(question) + + self.add(derivative, brace, question) diff --git a/eoc/chapter9.py b/eoc/chapter9.py new file mode 100644 index 00000000..70e637aa --- /dev/null +++ b/eoc/chapter9.py @@ -0,0 +1,70 @@ +from helpers import * +import scipy + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.fractals import * +from topics.number_line import * +from topics.combinatorics import * +from topics.numerals import * +from topics.three_dimensions import * +from topics.objects import * +from scene import Scene +from scene.zoomed_scene import ZoomedScene +from scene.reconfigurable_scene import ReconfigurableScene +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + +from eoc.graph_scene import GraphScene +from eoc.chapter1 import Thumbnail as Chapter1Thumbnail +from topics.common_scenes import OpeningQuote, PatreonThanks + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index 01a17326..5b7a1b01 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -76,7 +76,7 @@ class ThreeDCamera(CameraWithPerspective): three_d_status = map(is_3d, vmobs) has_points = [vm.get_num_points() > 0 for vm in vmobs] if all(three_d_status) and all(has_points): - cmp_vect = self.get_unit_normal_vect(vm) + cmp_vect = self.get_unit_normal_vect(vmobs[1]) return cmp(*[ np.dot(vm.get_center(), cmp_vect) for vm in vmobs @@ -98,8 +98,8 @@ def is_3d(mobject): return hasattr(mobject, "part_of_3d_mobject") class ThreeDMobject(VMobject): - def __init__(self, **kwargs): - VMobject.__init__(self, **kwargs) + def __init__(self, *args, **kwargs): + VMobject.__init__(self, *args, **kwargs) for submobject in self.submobject_family(): submobject.part_of_3d_mobject = True