Exemplo n.º 1
0
    def side_project(self, morty):
        rect = PictureInPictureFrame()
        rect.next_to(morty, UP+LEFT)
        side_project = TextMobject("Side project")
        side_project.next_to(rect, UP)
        dollar_sign = TexMobject("\\$")
        cross = VGroup(*[
            Line(vect, -vect, color = RED)
            for vect in UP+RIGHT, UP+LEFT
        ])
        cross.scale_to_fit_height(dollar_sign.get_height())
        no_money = VGroup(dollar_sign, cross)
        no_money.next_to(rect, DOWN)

        self.play(
            morty.change_mode, "raise_right_hand",
            morty.look_at, rect
        )
        self.play(
            Write(side_project),
            ShowCreation(rect)
        )
        self.dither()
        self.play(Blink(morty))
        self.dither()
        self.play(Write(dollar_sign))
        self.play(ShowCreation(cross))

        self.screen_title = side_project
        self.cross = cross
Exemplo n.º 2
0
 def generate_points(self):
     self.cell_height = self.height / self.nrows
     self.cell_width = self.width / self.nrows
     self.bottom_left = (self.cell_width * self.nrows / 2.0)*LEFT + \
                        (self.cell_height * self.nrows / 2.0)*DOWN
     num_to_num_mob   = {} 
     self.coords_to_mobs   = {}
     self.coords = [
         (n, k) 
         for n in range(self.nrows) 
         for k in range(n+1)
     ]
     for n, k in self.coords:
         num = choose(n, k)              
         center = self.coords_to_center(n, k)
         num_mob = TexMobject(str(num))
         scale_factor = min(
             1,
             self.portion_to_fill * self.cell_height / num_mob.get_height(),
             self.portion_to_fill * self.cell_width / num_mob.get_width(),
         )
         num_mob.center().scale(scale_factor).shift(center)
         if n not in self.coords_to_mobs:
             self.coords_to_mobs[n] = {}
         self.coords_to_mobs[n][k] = num_mob
     self.add(*[
         self.coords_to_mobs[n][k] 
         for n, k in self.coords
     ])
     return self
Exemplo n.º 3
0
    def side_project(self, morty):
        rect = PictureInPictureFrame()
        rect.next_to(morty, UP+LEFT)
        side_project = TextMobject("Side project")
        side_project.next_to(rect, UP)
        dollar_sign = TexMobject("\\$")
        cross = VGroup(*[
            Line(vect, -vect, color = RED)
            for vect in UP+RIGHT, UP+LEFT
        ])
        cross.scale_to_fit_height(dollar_sign.get_height())
        no_money = VGroup(dollar_sign, cross)
        no_money.next_to(rect, DOWN)

        self.play(
            morty.change_mode, "raise_right_hand",
            morty.look_at, rect
        )
        self.play(
            Write(side_project),
            ShowCreation(rect)
        )
        self.wait()
        self.play(Blink(morty))
        self.wait()
        self.play(Write(dollar_sign))
        self.play(ShowCreation(cross))

        self.screen_title = side_project
        self.cross = cross
Exemplo n.º 4
0
 def get_number_mobjects(self, *numbers):
     # TODO, handle decimals
     if len(numbers) == 0:
         numbers = self.default_numbers_to_display()
     result = []
     for number in numbers:
         mob = TexMobject(str(int(number)))
         vert_scale = 2 * self.tick_size / mob.get_height()
         hori_scale = self.tick_frequency * self.unit_length_to_spatial_width / mob.get_width()
         mob.scale(min(vert_scale, hori_scale))
         mob.shift(self.number_to_point(number))
         mob.shift(self.get_vertical_number_offset())
         result.append(mob)
     return result
Exemplo n.º 5
0
 def generate_n_choose_k_mobs(self):
     self.coords_to_n_choose_k = {}
     for n, k in self.coords:
         nck_mob = TexMobject(r"{%d \choose %d}" % (n, k))
         scale_factor = min(
             1,
             self.portion_to_fill * self.cell_height / nck_mob.get_height(),
             self.portion_to_fill * self.cell_width / nck_mob.get_width(),
         )
         center = self.coords_to_mobs[n][k].get_center()
         nck_mob.center().scale(scale_factor).shift(center)
         if n not in self.coords_to_n_choose_k:
             self.coords_to_n_choose_k[n] = {}
         self.coords_to_n_choose_k[n][k] = nck_mob
     return self
Exemplo n.º 6
0
 def generate_n_choose_k_mobs(self):
     self.coords_to_n_choose_k = {}
     for n, k in self.coords:
         nck_mob = TexMobject(r"{%d \choose %d}"%(n, k)) 
         scale_factor = min(
             1,
             self.portion_to_fill * self.cell_height / nck_mob.get_height(),
             self.portion_to_fill * self.cell_width / nck_mob.get_width(),
         )
         center = self.coords_to_mobs[n][k].get_center()
         nck_mob.center().scale(scale_factor).shift(center)
         if n not in self.coords_to_n_choose_k:
             self.coords_to_n_choose_k[n] = {}
         self.coords_to_n_choose_k[n][k] = nck_mob
     return self
Exemplo n.º 7
0
 def get_number_mobjects(self, *numbers):
     #TODO, handle decimals
     if len(numbers) == 0:
         numbers = self.default_numbers_to_display()
     result = []
     for number in numbers:
         mob = TexMobject(str(int(number)))
         vert_scale = 2 * self.tick_size / mob.get_height()
         hori_scale = self.tick_frequency * self.unit_length_to_spatial_width / mob.get_width(
         )
         mob.scale(min(vert_scale, hori_scale))
         mob.shift(self.number_to_point(number))
         mob.shift(self.get_vertical_number_offset())
         result.append(mob)
     return result
Exemplo n.º 8
0
    def scale_vector(self, v, factor, v_label, 
                     v_name = "v", factor_tex = None):
        starting_mobjects = list(self.mobjects)

        if factor_tex is None:
            factor_tex = str(factor)
        scaled_vector = self.add_vector(
            factor*v.get_end(), animate = False
        )
        self.remove(scaled_vector)
        label_tex = "%s\\vec{\\textbf{%s}}"%(factor_tex, v_name)
        label = self.label_vector(
            scaled_vector, label_tex, animate = False,
            add_to_vector = False
        )
        self.remove(label)
        factor_mob = TexMobject(factor_tex)
        if factor_mob.get_height() > 1:
            factor_mob.scale_to_fit_height(0.9)
        if factor_mob.get_width() > 1:
            factor_mob.scale_to_fit_width(0.9)
        factor_mob.shift(1.5*RIGHT+2.5*UP)

        num_factor_parts = len(factor_mob.split())
        factor_mob_parts_in_label = label.split()[:num_factor_parts]
        label_remainder_parts = label.split()[num_factor_parts:]
        factor_in_label = VMobject(*factor_mob_parts_in_label)
        label_remainder = VMobject(*label_remainder_parts)


        self.play(Write(factor_mob, run_time = 1))
        self.dither()
        self.play(
            ApplyMethod(v.copy().highlight, DARK_GREY),
            ApplyMethod(v_label.copy().highlight, DARK_GREY),
            Transform(factor_mob, factor_in_label),
            Transform(v.copy(), scaled_vector),
            Transform(v_label.copy(), label_remainder),
        )
        self.dither(2)

        self.clear()
        self.add(*starting_mobjects)
Exemplo n.º 9
0
    def scale_vector(self, v, factor, v_label, 
                     v_name = "v", factor_tex = None):
        starting_mobjects = list(self.mobjects)

        if factor_tex is None:
            factor_tex = str(factor)
        scaled_vector = self.add_vector(
            factor*v.get_end(), animate = False
        )
        self.remove(scaled_vector)
        label_tex = "%s\\vec{\\textbf{%s}}"%(factor_tex, v_name)
        label = self.label_vector(
            scaled_vector, label_tex, animate = False,
            add_to_vector = False
        )
        self.remove(label)
        factor_mob = TexMobject(factor_tex)
        if factor_mob.get_height() > 1:
            factor_mob.scale_to_fit_height(0.9)
        if factor_mob.get_width() > 1:
            factor_mob.scale_to_fit_width(0.9)
        factor_mob.shift(1.5*RIGHT+2.5*UP)

        num_factor_parts = len(factor_mob.split())
        factor_mob_parts_in_label = label.split()[:num_factor_parts]
        label_remainder_parts = label.split()[num_factor_parts:]
        factor_in_label = VMobject(*factor_mob_parts_in_label)
        label_remainder = VMobject(*label_remainder_parts)


        self.play(Write(factor_mob, run_time = 1))
        self.wait()
        self.play(
            ApplyMethod(v.copy().highlight, DARK_GREY),
            ApplyMethod(v_label.copy().highlight, DARK_GREY),
            Transform(factor_mob, factor_in_label),
            Transform(v.copy(), scaled_vector),
            Transform(v_label.copy(), label_remainder),
        )
        self.wait(2)

        self.clear()
        self.add(*starting_mobjects)
Exemplo n.º 10
0
    def construct(self):

        MAX_OPACITY = 0.4
        INDICATOR_RADIUS = 0.6
        OPACITY_FOR_UNIT_INTENSITY = 0.5

        A = np.array([5., -3., 0.])
        B = np.array([-5., 3., 0.])
        C = np.array([-5., -3., 0.])

        morty = self.get_primary_pi_creature()
        morty.scale(0.3).flip()
        right_pupil = morty.pupils[1]
        morty.next_to(C, LEFT, buff=0, submobject_to_align=right_pupil)

        horizontal = VMobject(stroke_width=1)
        horizontal.set_points_as_corners([C, A])
        vertical = VMobject(stroke_width=1)
        vertical.set_points_as_corners([C, B])

        self.play(ShowCreation(horizontal), ShowCreation(vertical))

        indicator = LightIndicator(
            color=LIGHT_COLOR,
            radius=INDICATOR_RADIUS,
            opacity_for_unit_intensity=OPACITY_FOR_UNIT_INTENSITY,
            show_reading=True,
            precision=2)

        indicator.next_to(morty, LEFT)

        self.play(Write(indicator))

        ls1 = LightSource(radius=20, num_levels=50)
        ls2 = ls1.deepcopy()
        #print "==="
        #print ls1.get_source_point()
        ls1.move_source_to(A)
        #print ls1.get_source_point()
        #print "==="
        #print ls2.get_source_point()
        ls2.move_source_to(B)
        #print ls2.get_source_point()

        self.play(FadeIn(ls1.lighthouse), FadeIn(ls2.lighthouse),
                  SwitchOn(ls1.ambient_light), SwitchOn(ls2.ambient_light))

        distance1 = np.linalg.norm(C - ls1.get_source_point())
        intensity = ls1.ambient_light.opacity_function(
            distance1) / indicator.opacity_for_unit_intensity
        distance2 = np.linalg.norm(C - ls2.get_source_point())
        intensity += ls2.ambient_light.opacity_function(
            distance2) / indicator.opacity_for_unit_intensity

        self.play(UpdateLightIndicator(indicator, intensity))

        self.wait()

        ls3 = ls1.deepcopy()
        ls3.move_to(np.array([6, 3.5, 0]))

        new_indicator = indicator.copy()
        new_indicator.light_source = ls3
        new_indicator.measurement_point = C
        self.add(new_indicator)
        self.play(indicator.shift, 2 * UP)

        #intensity = intensity_for_light_source(ls3)

        self.play(
            SwitchOff(ls1.ambient_light),
            #FadeOut(ls1.lighthouse),
            SwitchOff(ls2.ambient_light),
            #FadeOut(ls2.lighthouse),
            UpdateLightIndicator(new_indicator, 0.0))

        # create a *continual* animation for the replacement source
        updater = ContinualLightIndicatorUpdate(new_indicator)
        self.add(updater)

        self.play(
            SwitchOn(ls3.ambient_light),
            FadeIn(ls3.lighthouse),
        )

        self.wait()

        # move the light source around
        # TODO: moving along a path arc

        location = np.array([-3, -2., 0.])
        self.play(ls3.move_source_to, location)
        location = np.array([6., 1., 0.])
        self.play(ls3.move_source_to, location)
        location = np.array([5., 2., 0.])
        self.play(ls3.move_source_to, location)
        closer_location = interpolate(location, C, 0.5)
        self.play(ls3.move_source_to, closer_location)
        self.play(ls3.move_source_to, location)

        # maybe move in a circle around C using a loop?

        # find the coords of the altitude point H
        # as the solution of a certain LSE
        xA = A[0]
        yA = A[1]
        xB = B[0]
        yB = B[1]
        xC = C[0]
        yC = C[1]
        matrix = np.array([[yA - yB, xB - xA], [xA - xB, yA - yB]])  # sic
        vector = np.array([xB * yA - xA * yB, xC * (xA - xB) + yC * (yA - yB)])
        H2 = np.linalg.solve(matrix, vector)
        H = np.append(H2, 0.)

        self.play(ls3.move_source_to, H)

        # draw lines to complete the geometric picture
        # and label the lengths

        line_a = VMobject()
        line_a.set_points_as_corners([B, C])
        line_b = VMobject()
        line_b.set_points_as_corners([A, C])
        line_c = VMobject()
        line_c.set_points_as_corners([A, B])
        line_h = VMobject()
        line_h.set_points_as_corners([H, C])

        label_a = TexMobject("a")
        label_a.next_to(line_a, LEFT, buff=0.5)
        label_b = TexMobject("b")
        label_b.next_to(line_b, DOWN, buff=0.5)
        label_h = TexMobject("h")
        label_h.next_to(line_h.get_center(), RIGHT, buff=0.5)

        self.play(ShowCreation(line_a), Write(label_a))

        self.play(ShowCreation(line_b), Write(label_b))

        self.play(ShowCreation(line_c), )

        self.play(ShowCreation(line_h), Write(label_h))

        # state the IPT
        theorem_location = np.array([3., 2., 0.])
        theorem = TexMobject("{1\over a^2} + {1\over b^2} = {1\over h^2}")
        theorem_name = TextMobject("Inverse Pythagorean Theorem")
        buffer = 1.2
        theorem_box = Rectangle(width=buffer * theorem.get_width(),
                                height=buffer * theorem.get_height())

        theorem.move_to(theorem_location)
        theorem_box.move_to(theorem_location)
        theorem_name.next_to(theorem_box, UP)

        self.play(Write(theorem), )

        self.play(
            ShowCreation(theorem_box),
            Write(theorem_name),
        )
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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)