3b1b-manim/brachistochrone/multilayered.py

412 lines
11 KiB
Python
Raw Normal View History

2016-03-17 23:54:42 -07:00
import numpy as np
import itertools as it
from helpers import *
from mobject.tex_mobject import TexMobject, TextMobject, Brace
from mobject import Mobject, Mobject1D
from mobject.image_mobject import \
ImageMobject, MobjectFromPixelArray
from topics.three_dimensions import Stars
from animation import Animation
from animation.transform import *
from animation.simple_animations import *
from topics.geometry import *
from topics.characters import Randolph
from topics.functions import *
from mobject.region import Region
from scene import Scene
from scene.zoomed_scene import ZoomedScene
from camera import Camera
from brachistochrone.light import PhotonScene
from brachistochrone.curves import *
#Two to many
#race light in each
#n layers
#v_1, v_2, v_3
#proportional to sqrt y_1, y_2, y_3
#limiting process
#show sliding object and light
2016-03-17 23:54:42 -07:00
#which path is fastest
#instantaneously obey snell's law
class MultilayeredScene(Scene):
CONFIG = {
"n_layers" : 5,
"top_color" : BLUE_E,
"bottom_color" : BLUE_A,
"total_glass_height" : 5,
"top" : 3*UP,
"RectClass" : Rectangle #FilledRectangle
}
def get_layers(self, n_layers = None):
if n_layers is None:
n_layers = self.n_layers
width = 2*SPACE_WIDTH
height = float(self.total_glass_height)/n_layers
rgb_pair = [
np.array(Color(color).get_rgb())
for color in self.top_color, self.bottom_color
]
rgb_range = [
interpolate(*rgb_pair+[x])
for x in np.arange(0, 1, 1./n_layers)
]
tops = [
self.top + x*height*DOWN
for x in range(n_layers)
]
color = Color()
result = []
for top, rgb in zip(tops, rgb_range):
color.set_rgb(rgb)
rect = self.RectClass(
height = height,
width = width,
color = color
)
rect.shift(top-rect.get_top())
result.append(rect)
return result
def add_layers(self):
self.layers = self.get_layers()
self.add(*self.layers)
self.freeze_background()
def get_bottom(self):
return self.top + self.total_glass_height*DOWN
def get_continuous_glass(self):
result = self.RectClass(
width = 2*SPACE_WIDTH,
height = self.total_glass_height,
)
result.sort_points(lambda p : -p[1])
result.gradient_highlight(self.top_color, self.bottom_color)
result.shift(self.top-result.get_top())
return result
class TwoToMany(MultilayeredScene):
CONFIG = {
"RectClass" : FilledRectangle
}
def construct(self):
glass = self.get_glass()
layers = self.get_layers()
self.add(glass)
self.dither()
self.play(*[
FadeIn(
layer,
rate_func = squish_rate_func(smooth, x, 1)
)
for layer, x in zip(layers[1:], it.count(0, 0.2))
]+[
Transform(glass, layers[0])
])
self.dither()
def get_glass(self):
return self.RectClass(
height = SPACE_HEIGHT,
width = 2*SPACE_WIDTH,
color = BLUE_E
).shift(SPACE_HEIGHT*DOWN/2)
class RaceLightInLayers(MultilayeredScene, PhotonScene):
CONFIG = {
"RectClass" : FilledRectangle
}
def construct(self):
self.add_layers()
line = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT)
lines = [
line.copy().shift(layer.get_center())
for layer in self.layers
]
def rate_maker(x):
return lambda t : min(x*x*t, 1)
min_rate, max_rate = 1., 2.
rates = np.arange(min_rate, max_rate, (max_rate-min_rate)/self.n_layers)
self.play(*[
self.photon_run_along_path(
line,
rate_func = rate_maker(rate),
run_time = 2
)
for line, rate in zip(lines, rates)
])
class NLayers(MultilayeredScene):
CONFIG = {
"RectClass" : FilledRectangle
}
def construct(self):
self.add_layers()
brace = Brace(
Mobject(
Point(self.top),
Point(self.get_bottom())
),
RIGHT
)
n_layers = TextMobject("$n$ layers")
n_layers.next_to(brace)
self.dither()
self.add(brace)
self.show_frame()
self.play(
GrowFromCenter(brace),
GrowFromCenter(n_layers)
)
self.dither()
class ShowLayerVariables(MultilayeredScene, PhotonScene):
CONFIG = {
"RectClass" : FilledRectangle
}
def construct(self):
self.add_layers()
v_equations = []
start_ys = []
end_ys = []
center_paths = []
braces = []
for layer, x in zip(self.layers[:3], it.count(1)):
eq_mob = TexMobject(
["v_%d"%x, "=", "\sqrt{\phantom{y_1}}"],
size = "\\Large"
)
eq_mob.shift(layer.get_center()+2*LEFT)
v_eq = eq_mob.split()
v_eq[0].highlight(layer.get_color())
path = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT)
path.shift(layer.get_center())
brace_endpoints = Mobject(
Point(self.top),
Point(layer.get_bottom())
)
brace = Brace(brace_endpoints, RIGHT)
brace.shift(x*RIGHT)
start_y = TexMobject("y_%d"%x, size = "\\Large")
end_y = start_y.copy()
start_y.next_to(brace, RIGHT)
end_y.shift(v_eq[-1].get_center())
nudge = 0.2*RIGHT
end_y.shift(nudge)
v_equations.append(v_eq)
start_ys.append(start_y)
end_ys.append(end_y)
center_paths.append(path)
braces.append(brace)
for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]):
photon_run = self.photon_run_along_path(
path,
rate_func = None
)
self.play(
FadeToColor(v_eq[0], WHITE),
photon_run,
run_time = time
)
self.dither()
starts = [0, 0.3, 0.6]
self.play(*it.chain(*[
[
GrowFromCenter(
mob,
rate_func=squish_rate_func(smooth, start, 1)
)
for mob, start in zip(mobs, starts)
]
for mobs in start_ys, braces
]))
self.dither()
triplets = zip(v_equations, start_ys, end_ys)
anims = []
for v_eq, start_y, end_y in triplets:
anims += [
ShowCreation(v_eq[1]),
ShowCreation(v_eq[2]),
Transform(start_y.copy(), end_y)
]
self.play(*anims)
self.dither()
class LimitingProcess(MultilayeredScene):
CONFIG = {
"RectClass" : FilledRectangle
}
def construct(self):
num_iterations = 3
layer_sets = [
self.get_layers((2**x)*self.n_layers)
for x in range(num_iterations)
]
glass_sets = [
2016-03-17 23:54:42 -07:00
Mobject(*[
Mobject(
*layer_sets[x][(2**x)*index:(2**x)*(index+1)]
)
2016-03-17 23:54:42 -07:00
for index in range(self.n_layers)
]).ingest_sub_mobjects()
2016-03-17 23:54:42 -07:00
for x in range(num_iterations)
]
glass_sets.append(self.get_continuous_glass())
for glass_set in glass_sets:
glass_set.sort_points(lambda p : p[1])
curr_set = glass_sets[0]
2016-03-17 23:54:42 -07:00
self.add(curr_set)
for layer_set in glass_sets[1:]:
2016-03-17 23:54:42 -07:00
self.dither()
self.play(Transform(curr_set, layer_set))
self.dither()
class ShowLightAndSlidingObject(MultilayeredScene, TryManyPaths, PhotonScene):
CONFIG = {
"show_time" : False,
"dither_and_add" : False,
"RectClass" : FilledRectangle
}
def construct(self):
glass = self.get_continuous_glass()
self.play(ApplyMethod(glass.fade, 0.3))
self.freeze_background()
paths = self.get_paths()
for path in paths:
if path.get_height() > self.total_glass_height:
path.stretch(0.7, 1)
path.shift(self.top - path.get_top())
path.rgbs[:2] = 0
loop = paths.pop(1) ##Bad!
randy = Randolph()
randy.scale(RANDY_SCALE_VAL)
randy.shift(-randy.get_bottom())
photon_run = self.photon_run_along_path(
loop,
rate_func = lambda t : smooth(t, 3),
run_time = 4.1
)
text = self.get_text().to_edge(UP, buff = 0.2)
self.play(ShowCreation(loop))
self.dither()
self.play(photon_run)
self.remove(photon_run.mobject)
randy = self.slide(randy, loop)
self.add(randy)
self.dither()
self.remove(randy)
self.play(ShimmerIn(text))
for path in paths:
self.play(Transform(
loop, path,
path_func = path_along_arc(np.pi/2),
run_time = 2
))
class ContinuouslyObeyingSnellsLaw(MultilayeredScene, ZoomedScene):
CONFIG = {
"arc_radius" : 0.1,
"RectClass" : FilledRectangle
}
def construct(self):
glass = self.get_continuous_glass()
self.add(glass)
line = Line(
UP, DOWN,
density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D
)
theta = TexMobject("\\theta")
theta.scale(0.5/self.zoom_factor)
equation = TexMobject([
"\\dfrac{\\sin(\\theta)}",
"{\\sqrt{y}}",
" = \\text{constant}"
])
equation.next_to(Point(self.top), DOWN)
equation.shift(LEFT)
cycloid = Cycloid(end_theta = np.pi)
cycloid.highlight(YELLOW)
for mob in equation.split():
self.play(GrowFromCenter(mob), run_time = 0.5)
self.play(ShowCreation(cycloid))
self.activate_zooming()
little_square = self.get_zoomed_camera_mobject()
self.add(line)
indices = np.arange(
0, cycloid.get_num_points()-1, 10
)
for index in indices:
point = cycloid.points[index]
next_point = cycloid.points[index+1]
angle = angle_of_vector(point - next_point)-np.pi/2
for mob in little_square, line:
mob.shift(point - mob.get_center())
arc = Arc(angle, start_angle = np.pi/2)
arc.scale(self.arc_radius)
arc.shift(point)
color = Color()
height = (self.top-self.point)[1]
color.set_rgb([height/self.total_glass_height]*3)
for mob in arc, theta, line:
mob.highlight(color)
if angle > np.pi/15:
self.add(arc)
if angle > np.pi/6:
vect_angle = angle/2 + np.pi/2
vect = rotate_vector(RIGHT, vect_angle)
theta.center()
theta.shift(point)
theta.shift(1.5*self.arc_radius*vect)
self.add(theta)
self.dither(self.frame_duration)
self.remove(arc)
2016-03-17 23:54:42 -07:00