3b1b-manim/scripts/inventing_math.py

370 lines
11 KiB
Python
Raw Normal View History

2015-08-01 11:35:18 -07:00
#!/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)