diff --git a/scripts/inventing_math.py b/scripts/inventing_math.py new file mode 100644 index 00000000..f3054766 --- /dev/null +++ b/scripts/inventing_math.py @@ -0,0 +1,369 @@ +#!/usr/bin/env python + +import numpy as np +import itertools as it +from copy import deepcopy +import sys + + +from animation import * +from mobject import * +from constants import * +from region import * +from scene import Scene +from script_wrapper import command_line_create_scene + +MOVIE_PREFIX = "inventing_math/" +DIVERGENT_SUM_TEXT = [ + "1", + "+2", + "+4", + "+8", "+\\cdots", + "+2^n", + "+\\cdots", + "= -1", +] + +CONVERGENT_SUM_TEXT = [ + "\\frac{1}{2}", + "+\\frac{1}{4}", + "+\\frac{1}{8}", + "+\\frac{1}{16}", + "+\\cdots+", + "\\frac{1}{2^n}", + "+\\cdots", + "=1", +] +NUM_WRITTEN_TERMS = 4 +INTERVAL_RADIUS = 5 +NUM_INTERVAL_TICKS = 16 + +def divergent_sum(): + return tex_mobject(DIVERGENT_SUM_TEXT, size = "\\large").scale(2) + +def convergent_sum(): + return tex_mobject(CONVERGENT_SUM_TEXT, size = "\\large").scale(2) + +def underbrace(): + return tex_mobject("\\underbrace{%s}"%(8*"\\quad")) + +def zero_to_one_interval(): + interval = NumberLine( + radius = INTERVAL_RADIUS, + interval_size = 2.0*INTERVAL_RADIUS/NUM_INTERVAL_TICKS + ) + interval.elongate_tick_at(-INTERVAL_RADIUS, 4) + interval.elongate_tick_at(INTERVAL_RADIUS, 4) + zero = tex_mobject("0").shift(INTERVAL_RADIUS*LEFT+DOWN) + one = tex_mobject("1").shift(INTERVAL_RADIUS*RIGHT+DOWN) + return CompoundMobject(interval, zero, one) + +def draw_you(): + result = PiCreature() + result.give_straight_face().highlight("grey") + result.to_corner(LEFT+DOWN) + result.rewire_part_attributes() + return result + +class FlipThroughNumbers(Animation): + def __init__(self, function = lambda x : x, + start = 0, end = 10, + start_center = ORIGIN, + end_center = ORIGIN, + **kwargs): + self.function = function + self.start = start + self.end = end + self.start_center = start_center + self.end_center = end_center + self.current_number = function(start) + mobject = tex_mobject(str(self.current_number)).shift(start_center) + Animation.__init__(self, mobject, **kwargs) + + def update_mobject(self, alpha): + new_number = self.function( + self.start + int(alpha *(self.end-self.start)) + ) + if new_number != self.current_number: + self.current_number = new_number + self.mobject = tex_mobject(str(new_number)).shift(self.start_center) + if not all(self.start_center == self.end_center): + self.mobject.center().shift( + (1-alpha)*self.start_center + alpha*self.end_center + ) + + +###################################### + +class IntroduceDivergentSum(Scene): + def construct(self): + equation = divergent_sum().split() + sum_value = None + brace = underbrace().shift(0.75*DOWN) + min_x_coord = min(equation[0].points[:,0]) + for x in range(NUM_WRITTEN_TERMS): + self.add(equation[x]) + if x == 0: + self.dither(0.75) + continue + brace.stretch_to_fit_width( + max(equation[x].points[:,0]) - min_x_coord + ) + brace.to_edge(LEFT, buff = SPACE_WIDTH+min_x_coord) + if sum_value: + self.remove(sum_value) + sum_value = tex_mobject(str(2**(x+1) - 1)) + sum_value.shift(brace.get_center() + 0.5*DOWN) + self.add(brace, sum_value) + self.dither(0.75) + self.remove(sum_value) + ellipses = CompoundMobject( + *[equation[NUM_WRITTEN_TERMS + i] for i in range(3)] + ) + end_brace = deepcopy(brace).stretch_to_fit_width( + max(ellipses.points[:,0])-min_x_coord + ).to_edge(LEFT, buff = SPACE_WIDTH+min_x_coord) + kwargs = {"run_time" : 5.0, "alpha_func" : rush_into} + flip_through = FlipThroughNumbers( + lambda x : 2**(x+1)-1, + start = NUM_WRITTEN_TERMS-1, + end = 40, + start_center = brace.get_center() + 0.5*DOWN, + end_center = end_brace.get_center() + 0.5*DOWN, + **kwargs + ) + self.add(ellipses) + self.animate( + Transform(brace, end_brace, **kwargs), + flip_through, + ) + kwargs = {"run_time" : 0.5, "alpha_func" : rush_from} + self.clear() + self.add(CompoundMobject(*equation[:-1])) + self.animate( + Transform( + flip_through.mobject, + equation[-1], + **kwargs + ), + FadeOut(brace, **kwargs), + ) + self.dither() + +class ClearlyNonsense(Scene): + def construct(self): + number_line = NumberLine().add_numbers() + div_sum = divergent_sum() + this_way = text_mobject("Sum goes this way...") + this_way.to_edge(LEFT).shift(RIGHT*(SPACE_WIDTH+1) + DOWN) + how_here = text_mobject("How does it end up here?") + how_here.shift(1.5*UP+LEFT) + neg_1_arrow = Arrow( + (-1, 0.3, 0), + tail=how_here.get_center()+0.5*DOWN + ) + right_arrow = Arrow( + (SPACE_WIDTH-0.5)*RIGHT + DOWN, + tail = (max(this_way.points[:,0]), -1, 0) + ) + how_here.highlight("red") + neg_1_arrow.highlight("red") + this_way.highlight("yellow") + right_arrow.highlight("yellow") + + self.animate(Transform( + div_sum, + deepcopy(div_sum).scale(0.5).shift(3*UP) + )) + self.animate(ShowCreation(number_line)) + self.dither() + self.add(how_here) + self.animate(ShowCreation(neg_1_arrow)) + self.dither() + self.add(this_way) + self.animate(ShowCreation(right_arrow)) + self.dither() + +class ReasonsForMakingVideo(Scene): + def construct(self): + text = text_mobject([ + """ + \\begin{itemize} + \\item Understand what ``$ + """, + "".join(DIVERGENT_SUM_TEXT), + """ + $'' is saying. + """, + """ + \\item Nonsense-Driven Construction + \\end{itemize} + """ + ], size = "\\Small") + text.scale(1.5).to_edge(LEFT).shift(UP).highlight("white") + text.highlight("green", lambda (x, y, z) : x < -SPACE_WIDTH + 1) + line_one_first, equation, line_one_last, line_two = text.split() + line_two.shift(2*DOWN) + div_sum = divergent_sum().scale(0.5).shift(3*UP) + + self.add(div_sum) + self.animate( + ApplyMethod(div_sum.replace, equation), + FadeIn(line_one_first), + FadeIn(line_one_last) + ) + self.dither() + self.add(line_two) + self.dither() + +class YouAsMathematician(Scene): + def construct(self): + you = draw_you() + explanation = text_mobject( + "You as a (questionably accurate portrayal of a) mathematician.", + size = "\\small" + ).shift([2, you.get_center()[1], 0]) + arrow = Arrow(you.get_center(), direction = LEFT) + arrow.nudge(you.get_width()) + for mob in arrow, explanation: + mob.highlight("yellow") + bubble = ThoughtBubble() + bubble.stretch_to_fit_width(10) + bubble.pin_to(you) + equation = convergent_sum() + bubble.add_content(equation) + equation.shift(0.5*RIGHT) + bubble.clear() + dot_pair = [ + Dot(density = 3*DEFAULT_POINT_DENSITY_1D).shift(x+UP) + for x in LEFT, RIGHT + ] + self.add(you, explanation) + self.animate( + ShowCreation(arrow), + BlinkPiCreature(you) + ) + self.dither() + self.animate(ShowCreation(bubble)) + self.animate(ShowCreation(equation, run_time = 2.0)) + self.dither() + self.animate( + BlinkPiCreature(you), + FadeOut(explanation), + FadeOut(arrow) + ) + self.animate(Transform(equation, CompoundMobject(*dot_pair))) + self.remove(equation) + self.add(*dot_pair) + two_arrows = [ + Arrow(x, direction = x).shift(UP).nudge() + for x in LEFT, RIGHT + ] + self.animate(*[ShowCreation(a) for a in two_arrows]) + self.animate(BlinkPiCreature(you)) + self.remove(*dot_pair+two_arrows) + everything = CompoundMobject(*self.mobjects) + self.clear() + self.animate( + ApplyFunction( + everything, + lambda p : 3*SPACE_WIDTH*p/np.linalg.norm(p) + ), + *[ + Transform(dot, deepcopy(dot).shift(DOWN).scale(3)) + for dot in dot_pair + ], + run_time = 2.0 + ) + self.dither() + + +class DotsGettingCloser(Scene): + def construct(self): + dots = [ + Dot(radius = 3*Dot.DEFAULT_RADIUS).shift(3*x) + for x in LEFT, RIGHT + ] + self.add(*dots) + self.dither() + for x in range(10): + distance = min(dots[1].points[:,0])-max(dots[0].points[:,0]) + self.animate(ApplyMethod(dots[0].shift, 0.5*distance*RIGHT)) + + +class ZoomInOnInterval(Scene): + def construct(self): + number_line = NumberLine(density = 10*DEFAULT_POINT_DENSITY_1D) + number_line.add_numbers() + interval = zero_to_one_interval().split() + + new_line = deepcopy(number_line) + new_line.highlight("black", lambda (x,y,z) : x < 0 or x > 1 or y < -0.2) + # height = new_line.get_height() + new_line.scale(2*INTERVAL_RADIUS) + new_line.shift(INTERVAL_RADIUS*LEFT) + # new_line.stretch_to_fit_height(height) + + self.add(number_line) + self.dither() + self.animate(Transform(number_line, new_line)) + self.clear() + squish = lambda p : (p[0], 0, 0) + self.animate( + ApplyMethod(new_line.apply_function, squish), + ApplyMethod( + interval[0].apply_function, squish, + alpha_func = lambda t : 1-t + ), + *[FadeIn(interval[x]) for x in [1, 2]] + ) + self.clear() + self.add(*interval) + self.dither() + +class DanceDotOnInterval(Scene): + def construct(self): + interval = zero_to_one_interval() + dots = [ + Dot(radius = 3*Dot.DEFAULT_RADIUS).shift(INTERVAL_RADIUS*x+UP) + for x in LEFT, RIGHT + ] + color_range = Color("green").range_to("grey", NUM_WRITTEN_TERMS) + conv_sum = convergent_sum().split() + + self.add(interval) + self.animate(*[ + ApplyMethod(dot.shift, DOWN) + for dot in dots + ]) + self.dither() + for count in range(NUM_WRITTEN_TERMS): + shift_val = RIGHT*INTERVAL_RADIUS/(2.0**count) + start = dots[0].get_center() + line = Line(start, start + shift_val*RIGHT) + line.highlight(color_range.next()) + self.animate( + ApplyMethod(dots[0].shift, shift_val), + ShowCreation(line) + ) + num = conv_sum[count] + num.shift(RIGHT*(line.get_center()[0]-num.get_center()[0])) + if num.get_width() > line.get_length(): + num.stretch_to_fit_width(line.get_length()) + num.shift(3*DOWN) + self.animate( + ApplyMethod(line.shift, 2*DOWN), + FadeIn(num) + ) + self.dither() + self.animate(FadeIn(conv_sum[NUM_WRITTEN_TERMS])) + + + + + + + +if __name__ == "__main__": + command_line_create_scene(MOVIE_PREFIX) +