Files
manim/hilbert/section1.py
2015-12-22 11:04:56 -08:00

710 lines
20 KiB
Python

from mobject import Mobject, Point
from mobject.tex_mobject import \
TexMobject, TextMobject, Brace
from mobject.image_mobject import \
ImageMobject, MobjectFromRegion
from scene import Scene
from animation import Animation
from animation.transform import \
Transform, CounterclockwiseTransform, ApplyMethod,\
GrowFromCenter, ClockwiseTransform, ApplyPointwiseFunction
from animation.simple_animations import \
ShowCreation, ShimmerIn, FadeOut, FadeIn
from animation.meta_animations import \
DelayByOrder, TransformAnimations
from animation.playground import Vibrate
from topics.geometry import \
Line, Dot, Arrow, Grid, Square, Point
from topics.characters import \
ThoughtBubble, SpeechBubble, Mathematician
from topics.number_line import UnitInterval
from topics.three_dimensions import Stars
from region import region_from_polygon_vertices
import displayer as disp
from hilbert.curves import \
TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \
SnakeCurve
from helpers import *
def get_grid():
return Grid(64, 64)
def get_freq_line():
return UnitInterval().shift(2*DOWN) ##Change?
class AboutSpaceFillingCurves(TransformOverIncreasingOrders):
@staticmethod
def args_to_string():
return ""
@staticmethod
def string_to_args(arg_str):
return ()
def construct(self):
self.bubble = ThoughtBubble().ingest_sub_mobjects()
self.bubble.scale(1.5)
TransformOverIncreasingOrders.construct(self, FlowSnake, 7)
self.play(Transform(self.curve, self.bubble))
self.show_infinite_objects()
self.pose_question()
self.dither()
def show_infinite_objects(self):
sigma, summand, equals, result = TexMobject([
"\\sum_{n = 1}^{\\infty}",
"\\dfrac{1}{n^2}",
"=",
"\\dfrac{\pi^2}{6}"
]).split()
alt_summand = TexMobject("n").replace(summand)
alt_result = TexMobject("-\\dfrac{1}{12}").replace(result)
rationals, other_equals, naturals = TexMobject([
"|\\mathds{Q}|",
"=",
"|\\mathds{N}|"
]).scale(2).split()
infinity = TexMobject("\\infty").scale(2)
local_mobjects = filter(
lambda m : isinstance(m, Mobject),
locals().values(),
)
for mob in local_mobjects:
mob.sort_points(np.linalg.norm)
self.play(ShimmerIn(infinity))
self.dither()
self.play(
ShimmerIn(summand),
ShimmerIn(equals),
ShimmerIn(result),
DelayByOrder(Transform(infinity, sigma))
)
self.dither()
self.play(
Transform(summand, alt_summand),
Transform(result, alt_result),
)
self.dither()
self.remove(infinity)
self.play(*[
CounterclockwiseTransform(
Mobject(summand, equals, result, sigma),
Mobject(rationals, other_equals, naturals)
)
])
self.dither()
self.clear()
self.add(self.bubble)
def pose_question(self):
infinity, rightarrow, N = TexMobject([
"\\infty", "\\rightarrow", "N"
]).scale(2).split()
question_mark = TextMobject("?").scale(2)
self.add(question_mark)
self.dither()
self.play(*[
ShimmerIn(mob)
for mob in infinity, rightarrow, N
] + [
ApplyMethod(question_mark.next_to, rightarrow, UP),
])
self.dither()
class PostponePhilosophizing(Scene):
def construct(self):
abstract, arrow, concrete = TextMobject([
"Abstract", " $\\rightarrow$ ", "Concrete"
]).scale(2).split()
self.add(abstract, arrow, concrete)
self.dither()
self.play(*[
ApplyMethod(
word1.replace, word2,
interpolation_function = path_along_arc(np.pi/2)
)
for word1, word2 in it.permutations([abstract, concrete])
])
self.dither()
class SectionOne(Scene):
def construct(self):
self.add(TextMobject("Section 1: Seeing with your ears"))
self.dither()
class WriteSomeSoftware(Scene):
pass #Done viea screen capture, written here for organization
class ImageToSound(Scene):
def construct(self):
string = Vibrate(color = BLUE_D, run_time = 5)
picture = ImageMobject("lion", invert = False)
picture.scale(0.8)
picture_copy = picture.copy()
picture.sort_points(np.linalg.norm)
string.mobject.sort_points(lambda p : -np.linalg.norm(p))
self.add(picture)
self.dither()
self.play(Transform(
picture, string.mobject,
run_time = 3,
alpha_func = rush_into
))
self.remove(picture)
self.play(string)
for mob in picture_copy, string.mobject:
mob.sort_points(lambda p : np.linalg.norm(p)%1)
self.play(Transform(
string.mobject, picture_copy,
run_time = 5,
alpha_func = rush_from
))
class ImageDataIsTwoDimensional(Scene):
def construct(self):
image = ImageMobject("lion", invert = False)
image.scale(0.5)
image.shift(2*LEFT)
self.add(image)
for vect, num in zip([DOWN, RIGHT], [1, 2]):
brace = Brace(image, vect)
words_mob = TextMobject("Dimension %d"%num)
words_mob.next_to(image, vect, buff = 1)
self.play(
Transform(Point(brace.get_center()), brace),
ShimmerIn(words_mob),
run_time = 2
)
self.dither()
class SoundDataIsOneDimensional(Scene):
def construct(self):
overtones = 5
floor = 2*DOWN
main_string = Vibrate(color = BLUE_D)
component_strings = [
Vibrate(
num_periods = k+1,
overtones = 1,
color = color,
center = 2*DOWN + UP*k
)
for k, color in zip(
range(overtones),
Color(BLUE_E).range_to(WHITE, overtones)
)
]
dots = [
Dot(
string.mobject.get_center(),
color = string.mobject.get_color()
)
for string in component_strings
]
freq_line = get_freq_line()
freq_line.shift(floor)
freq_line.sort_points(np.linalg.norm)
brace = Brace(freq_line, UP)
words = TextMobject("Range of frequency values")
words.next_to(brace, UP)
self.play(*[
TransformAnimations(
main_string.copy(),
string,
run_time = 5
)
for string in component_strings
])
self.clear()
self.play(*[
TransformAnimations(
string,
Animation(dot)
)
for string, dot in zip(component_strings, dots)
])
self.clear()
self.play(
ShowCreation(freq_line),
GrowFromCenter(brace),
ShimmerIn(words),
*[
Transform(
dot,
dot.copy().scale(2).rotate(-np.pi/2).shift(floor),
interpolation_function = path_along_arc(np.pi/3)
)
for dot in dots
]
)
self.dither(0.5)
class GridOfPixels(Scene):
def construct(self):
low_res = ImageMobject("low_resolution_lion", invert = False)
high_res = ImageMobject("Lion", invert = False)
grid = get_grid().scale(0.8)
for mob in low_res, high_res:
mob.replace(grid, stretch = True)
side_brace = Brace(low_res, LEFT)
top_brace = Brace(low_res, UP)
top_words = TextMobject("256 Px", size = "\\normal")
side_words = top_words.copy().rotate(np.pi/2)
top_words.next_to(top_brace, UP)
side_words.next_to(side_brace, LEFT)
self.add(high_res)
self.dither()
self.play(DelayByOrder(Transform(high_res, low_res)))
self.dither()
self.play(
GrowFromCenter(top_brace),
GrowFromCenter(side_brace),
ShimmerIn(top_words),
ShimmerIn(side_words)
)
self.dither()
for mob in grid, high_res:
mob.sort_points(np.linalg.norm)
self.play(DelayByOrder(Transform(high_res, grid)))
self.dither()
class ShowFrequencySpace(Scene):
def construct(self):
freq_line = get_freq_line()
self.add(freq_line)
self.dither()
for tex, vect in zip(["20 Hz", "20{,}000 Hz"], [LEFT, RIGHT]):
tex_mob = TextMobject(tex)
tex_mob.to_edge(vect)
tex_mob.shift(UP)
arrow = Arrow(tex_mob, freq_line.get_edge_center(vect))
self.play(
ShimmerIn(tex_mob),
ShowCreation(arrow)
)
self.dither()
class AssociatePixelWithFrequency(Scene):
def construct(self):
big_grid_dim = 20.
small_grid_dim = 6.
big_grid = Grid(64, 64, height = big_grid_dim, width = big_grid_dim)
big_grid.to_corner(UP+RIGHT, buff = 2)
small_grid = big_grid.copy()
small_grid.scale(small_grid_dim/big_grid_dim)
small_grid.center()
pixel = MobjectFromRegion(
region_from_polygon_vertices(*0.2*np.array([
RIGHT+DOWN,
RIGHT+UP,
LEFT+UP,
LEFT+DOWN
]))
)
pixel.set_color(WHITE)
pixel_width = big_grid.width/big_grid.columns
pixel.scale_to_fit_width(pixel_width)
pixel.to_corner(UP+RIGHT, buff = 2)
pixel.shift(5*pixel_width*(2*LEFT+DOWN))
freq_line = get_freq_line()
dot = Dot()
dot.shift(freq_line.get_center() + 2*RIGHT)
string = Line(LEFT, RIGHT, color = GREEN)
arrow = Arrow(
dot, string.get_center(),
color = YELLOW_C
)
vibration_config = {
"overtones" : 1,
"spatial_period" : 2,
}
vibration, loud_vibration, quiet_vibration = [
Vibrate(string.copy(), amplitude = a, **vibration_config)
for a in [0.5, 1., 0.25]
]
self.add(small_grid)
self.dither()
self.play(
Transform(small_grid, big_grid)
)
self.play(FadeIn(pixel))
self.dither()
self.play(
FadeOut(small_grid),
ShowCreation(freq_line)
)
self.remove(small_grid)
self.play(
Transform(pixel, dot),
)
self.dither()
self.play(ShowCreation(arrow))
self.play(loud_vibration)
self.play(
TransformAnimations(loud_vibration, quiet_vibration),
ApplyMethod(dot.fade, 0.9)
)
self.clear()
self.add(freq_line, dot, arrow)
self.play(quiet_vibration)
class ListenToAllPixels(Scene):
def construct(self):
grid = get_grid()
grid.sort_points(np.linalg.norm)
freq_line = get_freq_line()
freq_line.sort_points(lambda p : p[0])
red, blue = Color(RED), Color(BLUE)
freq_line.gradient_highlight(red, blue)
colors = [
Color(rgb = interpolate(
np.array(red.rgb),
np.array(blue.rgb),
alpha
))
for alpha in np.arange(4)/3.
]
string = Line(3*LEFT, 3*RIGHT, color = colors[1])
vibration = Vibrate(string)
vibration_copy = vibration.copy()
vibration_copy.mobject.point_thickness = 1
sub_vibrations = [
Vibrate(
string.copy().shift((n-1)*UP).highlight(colors[n]),
overtones = 1,
spatial_period = 6./(n+1),
temporal_period = 1./(n+1),
amplitude = 0.5/(n+1)
)
for n in range(4)
]
words = TexMobject("&\\vdots \\\\ \\text{thousands }& \\text{of frequencies} \\\\ &\\vdots")
words.to_edge(UP, buff = 0.1)
self.add(grid)
self.dither()
self.play(DelayByOrder(ApplyMethod(
grid.gradient_highlight, red, blue
)))
self.play(Transform(grid, freq_line))
self.dither()
self.play(
ShimmerIn(
words,
alpha_func = squish_alpha_func(smooth, 0, 0.2)
),
*sub_vibrations,
run_time = 5
)
self.play(
*[
TransformAnimations(
sub_vib, vibration
)
for sub_vib in sub_vibrations
]+[FadeOut(words)]
)
self.clear()
self.add(freq_line)
self.play(vibration)
class LayAsideSpeculation(Scene):
def construct(self):
words = TextMobject("Would this actually work?")
grid = get_grid()
grid.scale_to_fit_width(6)
grid.to_edge(LEFT)
freq_line = get_freq_line()
freq_line.scale_to_fit_width(6)
freq_line.center().to_edge(RIGHT)
mapping = Mobject(
grid, freq_line, Arrow(grid, freq_line)
)
mapping.ingest_sub_mobjects()
lower_left = Point().to_corner(DOWN+LEFT, buff = 0)
lower_right = Point().to_corner(DOWN+RIGHT, buff = 0)
self.add(words)
self.dither()
self.play(
Transform(words, lower_right),
Transform(lower_left, mapping)
)
self.dither()
class RandomMapping(Scene):
def construct(self):
grid = get_grid()
grid.scale_to_fit_width(6)
grid.to_edge(LEFT)
freq_line = get_freq_line()
freq_line.scale_to_fit_width(6)
freq_line.center().to_edge(RIGHT)
# for mob in grid, freq_line:
# indices = np.arange(mob.get_num_points())
# random.shuffle(indices)
# mob.points = mob.points[indices]
stars = Stars(point_thickness = grid.point_thickness)
self.add(grid)
targets = [stars, freq_line]
alphas = [not_quite_there(rush_into), rush_from]
for target, alpha_func in zip(targets, alphas):
self.play(Transform(
grid, target,
run_time = 3,
alpha_func = alpha_func,
interpolation_function = path_along_arc(-np.pi/2)
))
self.dither()
class LeverageExistingIntuitions(Scene):
def construct(self):
self.add(TextMobject("Leverage existing intuitions"))
self.dither()
class ThinkInTermsOfReverseMapping(Scene):
def construct(self):
grid = get_grid()
grid.scale_to_fit_width(6)
grid.to_edge(LEFT)
freq_line = get_freq_line()
freq_line.scale_to_fit_width(6)
freq_line.center().to_edge(RIGHT)
arrow = Arrow(grid, freq_line)
color1, color2 = YELLOW_C, RED
square_length = 0.01
dot1 = Dot(color = color1)
dot1.shift(3*RIGHT)
dot2 = Dot(color = color2)
dot2.shift(3.1*RIGHT)
arrow1 = Arrow(2*RIGHT+UP, dot1, color = color1, buffer = 0.1)
arrow2 = Arrow(4*RIGHT+UP, dot2, color = color2, buffer = 0.1)
dot3, arrow3 = [
mob.copy().shift(5*LEFT+UP)
for mob in dot1, arrow1
]
dot4, arrow4 = [
mob.copy().shift(5*LEFT+0.9*UP)
for mob in dot2, arrow2
]
self.add(grid, freq_line, arrow)
self.dither()
self.play(ApplyMethod(
arrow.rotate, np.pi,
interpolation_function = clockwise_path()
))
self.dither()
self.play(ShowCreation(arrow1))
self.add(dot1)
self.play(ShowCreation(arrow2))
self.add(dot2)
self.dither()
self.remove(arrow1, arrow2)
self.play(
Transform(dot1, dot3),
Transform(dot2, dot4)
)
self.play(
ApplyMethod(grid.fade, 0.8),
Animation(Mobject(dot3, dot4))
)
self.play(ShowCreation(arrow3))
self.play(ShowCreation(arrow4))
self.dither()
class WeaveLineThroughPixels(Scene):
def construct(self):
start_color, end_color = RED, GREEN
curve = HilbertCurve(order = 2)
line = Line(5*LEFT, 5*RIGHT)
for mob in curve, line:
mob.gradient_highlight(start_color, end_color)
freq_line = get_freq_line()
freq_line.replace(line, stretch = True)
unit = 6./4 #sidelength of pixel
up = unit*UP
right = unit*RIGHT
lower_left = 3*(LEFT+DOWN)
squares = Mobject(*[
Square(
side_length = unit,
color = WHITE
).shift(x*right+y*up)
for x, y in it.product(range(4), range(4))
])
squares.center()
targets = Mobject()
for square in squares.sub_mobjects:
center = square.get_center()
distances = np.apply_along_axis(
lambda p : np.linalg.norm(p-center),
1,
curve.points
)
index_along_curve = np.argmin(distances)
fraction_along_curve = index_along_curve/float(curve.get_num_points())
target = square.copy().center().scale(0.2)
line_index = int(fraction_along_curve*line.get_num_points())
target.shift(line.points[line_index])
targets.add(target)
self.add(squares)
self.play(ShowCreation(
curve,
run_time = 5,
alpha_func = None
))
self.dither()
self.play(
Transform(curve, line),
Transform(squares, targets),
run_time = 3
)
self.dither()
self.play(ShowCreation(freq_line))
self.dither()
class WellPlayedGameOfSnake(Scene):
def construct(self):
grid = Grid(16, 16).fade()
snake_curve = SnakeCurve(order = 4)
words = TextMobject("``Snake Curve''")
words.next_to(grid, UP)
self.add(grid)
self.play(ShowCreation(
snake_curve,
run_time = 7,
alpha_func = None
))
self.dither()
self.play(ShimmerIn(words))
self.dither()
class TellMathematicianFriend(Scene):
def construct(self):
mathy = Mathematician()
mathy.to_edge(DOWN).shift(4*LEFT)
squiggle_mouth = mathy.mouth.copy()
squiggle_mouth.apply_function(
lambda (x, y, z) : (x, y+0.02*np.sin(50*x), z)
)
bubble = SpeechBubble(initial_width = 8)
bubble.pin_to(mathy)
bubble.ingest_sub_mobjects()
bubble.write("Why not use a Hilbert curve \\textinterrobang ")
words1 = bubble.content
bubble.write("So, it's not one curve but an infinite family of curves \\dots")
words2 = bubble.content
bubble.write("Well, no, it \\emph{is} just one thing, but I need \\\\ \
to tell you about a certain infinite family first.")
words3 = bubble.content
description = TextMobject("Mathematician friend", size = "\\small")
description.next_to(mathy, buff = 2)
arrow = Arrow(description, mathy)
self.add(mathy)
self.play(
ShowCreation(arrow),
ShimmerIn(description)
)
self.dither()
point = Point(bubble.get_tip())
self.play(
Transform(point, bubble),
)
self.remove(point)
self.add(bubble)
self.play(ShimmerIn(words1))
self.dither()
self.remove(description, arrow)
self.play(
Transform(mathy.mouth, squiggle_mouth),
ApplyMethod(mathy.arm.wag, 0.2*RIGHT, LEFT),
)
self.remove(words1)
self.add(words2)
self.dither(2)
self.remove(words2)
self.add(words3)
self.dither(2)
self.play(
ApplyPointwiseFunction(
lambda p : 15*p/np.linalg.norm(p),
bubble
),
ApplyMethod(mathy.shift, 5*(DOWN+LEFT)),
FadeOut(words3),
run_time = 3
)
class PseudoHilbertCurves(Scene):
@staticmethod
def args_to_string(order):
return "Order%d"%order
@staticmethod
def string_to_args(order_str):
return int(order_str)
def construct(self, order):
pass