from helpers import * 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 * class LastVideo(TeacherStudentsScene): def construct(self): series = VideoSeries() series.to_edge(UP) last_video = series[2] next_video = series[3] last_video_color = last_video[0].get_fill_color() early_videos = VGroup(*series[:3]) later_videos = VGroup(*series[3:]) this_video = VideoIcon().scale(0.5) this_video.move_to(VGroup(last_video, next_video), DOWN) 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( 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"] ) self.dither(2) self.play(known_formulas.replace, last_video) last_video.add(known_formulas) this_video_copy = this_video.copy() self.play( early_videos.stretch_to_fit_width, early_videos.get_width() - this_video_copy.get_width(), early_videos.next_to, this_video_copy, LEFT, SMALL_BUFF, DOWN, later_videos.stretch_to_fit_width, later_videos.get_width() - this_video_copy.get_width(), later_videos.next_to, this_video_copy, RIGHT, SMALL_BUFF, DOWN, last_video_brace.stretch_to_fit_width, this_video_copy.get_width(), last_video_brace.next_to, this_video_copy, DOWN, SMALL_BUFF, GrowFromCenter(this_video) ) self.play( last_video.highlight, last_video_color, this_video.highlight, YELLOW ) self.play( FadeOut(self.get_students()[-1].bubble), exp_question.next_to, last_video_brace, DOWN, *[ ApplyMethod(pi.change_mode, "pondering") for pi in self.get_students() ] ) 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() 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) ) 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) 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 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() #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" : 3, "base_str" : "3", } class ExponentRatioWithE(AnalyzeExponentRatio): CONFIG = { "base" : np.exp(1), "base_str" : "e", }