mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
326 lines
11 KiB
Python
326 lines
11 KiB
Python
import numpy as np
|
|
import itertools as it
|
|
|
|
from manimlib.imports import *
|
|
|
|
from old_projects.brachistochrone.curves import Cycloid
|
|
|
|
class MultilayeredGlass(PhotonScene, ZoomedScene):
|
|
CONFIG = {
|
|
"num_discrete_layers" : 5,
|
|
"num_variables" : 3,
|
|
"top_color" : BLUE_E,
|
|
"bottom_color" : BLUE_A,
|
|
"zoomed_canvas_frame_shape" : (5, 5),
|
|
"square_color" : GREEN_B,
|
|
}
|
|
def construct(self):
|
|
self.cycloid = Cycloid(end_theta = np.pi)
|
|
self.cycloid.set_color(YELLOW)
|
|
self.top = self.cycloid.get_top()[1]
|
|
self.bottom = self.cycloid.get_bottom()[1]-1
|
|
self.generate_layers()
|
|
self.generate_discrete_path()
|
|
photon_run = self.photon_run_along_path(
|
|
self.discrete_path,
|
|
run_time = 1,
|
|
rate_func = rush_into
|
|
)
|
|
|
|
|
|
|
|
self.continuous_to_smooth()
|
|
self.add(*self.layers)
|
|
self.show_layer_variables()
|
|
self.play(photon_run)
|
|
self.play(ShowCreation(self.discrete_path))
|
|
self.isolate_bend_points()
|
|
self.clear()
|
|
self.add(*self.layers)
|
|
self.show_main_equation()
|
|
self.ask_continuous_question()
|
|
|
|
def continuous_to_smooth(self):
|
|
self.add(*self.layers)
|
|
continuous = self.get_continuous_background()
|
|
self.add(continuous)
|
|
self.wait()
|
|
self.play(ShowCreation(
|
|
continuous,
|
|
rate_func = lambda t : smooth(1-t)
|
|
))
|
|
self.remove(continuous)
|
|
self.wait()
|
|
|
|
def get_continuous_background(self):
|
|
glass = FilledRectangle(
|
|
height = self.top-self.bottom,
|
|
width = FRAME_WIDTH,
|
|
)
|
|
glass.sort_points(lambda p : -p[1])
|
|
glass.shift((self.top-glass.get_top()[1])*UP)
|
|
glass.set_color_by_gradient(self.top_color, self.bottom_color)
|
|
return glass
|
|
|
|
def generate_layer_info(self):
|
|
self.layer_thickness = float(self.top-self.bottom)/self.num_discrete_layers
|
|
self.layer_tops = np.arange(
|
|
self.top, self.bottom, -self.layer_thickness
|
|
)
|
|
top_rgb, bottom_rgb = [
|
|
np.array(Color(color).get_rgb())
|
|
for color in (self.top_color, self.bottom_color)
|
|
]
|
|
epsilon = 1./(self.num_discrete_layers-1)
|
|
self.layer_colors = [
|
|
Color(rgb = interpolate(top_rgb, bottom_rgb, alpha))
|
|
for alpha in np.arange(0, 1+epsilon, epsilon)
|
|
]
|
|
|
|
def generate_layers(self):
|
|
self.generate_layer_info()
|
|
def create_region(top, color):
|
|
return Region(
|
|
lambda x, y : (y < top) & (y > top-self.layer_thickness),
|
|
color = color
|
|
)
|
|
self.layers = [
|
|
create_region(top, color)
|
|
for top, color in zip(self.layer_tops, self.layer_colors)
|
|
]
|
|
|
|
|
|
def generate_discrete_path(self):
|
|
points = self.cycloid.points
|
|
tops = list(self.layer_tops)
|
|
tops.append(tops[-1]-self.layer_thickness)
|
|
indices = [
|
|
np.argmin(np.abs(points[:, 1]-top))
|
|
for top in tops
|
|
]
|
|
self.bend_points = points[indices[1:-1]]
|
|
self.path_angles = []
|
|
self.discrete_path = Mobject1D(
|
|
color = YELLOW,
|
|
density = 3*DEFAULT_POINT_DENSITY_1D
|
|
)
|
|
for start, end in zip(indices, indices[1:]):
|
|
start_point, end_point = points[start], points[end]
|
|
self.discrete_path.add_line(
|
|
start_point, end_point
|
|
)
|
|
self.path_angles.append(
|
|
angle_of_vector(start_point-end_point)-np.pi/2
|
|
)
|
|
self.discrete_path.add_line(
|
|
points[end], FRAME_X_RADIUS*RIGHT+(self.layer_tops[-1]-1)*UP
|
|
)
|
|
|
|
def show_layer_variables(self):
|
|
layer_top_pairs = list(zip(
|
|
self.layer_tops[:self.num_variables],
|
|
self.layer_tops[1:]
|
|
))
|
|
v_equations = []
|
|
start_ys = []
|
|
end_ys = []
|
|
center_paths = []
|
|
braces = []
|
|
for (top1, top2), x in zip(layer_top_pairs, it.count(1)):
|
|
eq_mob = TexMobject(
|
|
["v_%d"%x, "=", "\sqrt{\phantom{y_1}}"],
|
|
size = "\\Large"
|
|
)
|
|
midpoint = UP*(top1+top2)/2
|
|
eq_mob.shift(midpoint)
|
|
v_eq = eq_mob.split()
|
|
center_paths.append(Line(
|
|
midpoint+FRAME_X_RADIUS*LEFT,
|
|
midpoint+FRAME_X_RADIUS*RIGHT
|
|
))
|
|
brace_endpoints = Mobject(
|
|
Point(self.top*UP+x*RIGHT),
|
|
Point(top2*UP+x*RIGHT)
|
|
)
|
|
brace = Brace(brace_endpoints, 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())
|
|
end_y.shift(0.2*RIGHT)
|
|
|
|
v_equations.append(v_eq)
|
|
start_ys.append(start_y)
|
|
end_ys.append(end_y)
|
|
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=linear
|
|
)
|
|
self.play(
|
|
ShimmerIn(v_eq[0]),
|
|
photon_run,
|
|
run_time = time
|
|
)
|
|
self.wait()
|
|
for start_y, brace in zip(start_ys, braces):
|
|
self.add(start_y)
|
|
self.play(GrowFromCenter(brace))
|
|
self.wait()
|
|
quads = list(zip(v_equations, start_ys, end_ys, braces))
|
|
self.equations = []
|
|
for v_eq, start_y, end_y, brace in quads:
|
|
self.remove(brace)
|
|
self.play(
|
|
ShowCreation(v_eq[1]),
|
|
ShowCreation(v_eq[2]),
|
|
Transform(start_y, end_y)
|
|
)
|
|
|
|
v_eq.append(start_y)
|
|
self.equations.append(Mobject(*v_eq))
|
|
|
|
def isolate_bend_points(self):
|
|
arc_radius = 0.1
|
|
self.activate_zooming()
|
|
little_square = self.get_zoomed_camera_mobject()
|
|
|
|
for index in range(3):
|
|
bend_point = self.bend_points[index]
|
|
line = Line(
|
|
bend_point+DOWN,
|
|
bend_point+UP,
|
|
color = WHITE,
|
|
density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D
|
|
)
|
|
angle_arcs = []
|
|
for i, rotation in [(index, np.pi/2), (index+1, -np.pi/2)]:
|
|
arc = Arc(angle = self.path_angles[i])
|
|
arc.scale(arc_radius)
|
|
arc.rotate(rotation)
|
|
arc.shift(bend_point)
|
|
angle_arcs.append(arc)
|
|
thetas = []
|
|
for i in [index+1, index+2]:
|
|
theta = TexMobject("\\theta_%d"%i)
|
|
theta.scale(0.5/self.zoom_factor)
|
|
vert = UP if i == index+1 else DOWN
|
|
horiz = rotate_vector(vert, np.pi/2)
|
|
theta.next_to(
|
|
Point(bend_point),
|
|
horiz,
|
|
buff = 0.01
|
|
)
|
|
theta.shift(1.5*arc_radius*vert)
|
|
thetas.append(theta)
|
|
figure_marks = [line] + angle_arcs + thetas
|
|
|
|
self.play(ApplyMethod(
|
|
little_square.shift,
|
|
bend_point - little_square.get_center(),
|
|
run_time = 2
|
|
))
|
|
self.play(*list(map(ShowCreation, figure_marks)))
|
|
self.wait()
|
|
equation_frame = little_square.copy()
|
|
equation_frame.scale(0.5)
|
|
equation_frame.shift(
|
|
little_square.get_corner(UP+RIGHT) - \
|
|
equation_frame.get_corner(UP+RIGHT)
|
|
)
|
|
equation_frame.scale_in_place(0.9)
|
|
self.show_snells(index+1, equation_frame)
|
|
self.remove(*figure_marks)
|
|
self.disactivate_zooming()
|
|
|
|
def show_snells(self, index, frame):
|
|
left_text, right_text = [
|
|
"\\dfrac{\\sin(\\theta_%d)}{\\phantom{\\sqrt{y_1}}}"%x
|
|
for x in (index, index+1)
|
|
]
|
|
left, equals, right = TexMobject(
|
|
[left_text, "=", right_text]
|
|
).split()
|
|
vs = []
|
|
sqrt_ys = []
|
|
for x, numerator in [(index, left), (index+1, right)]:
|
|
v, sqrt_y = [
|
|
TexMobject(
|
|
text, size = "\\Large"
|
|
).next_to(numerator, DOWN)
|
|
for text in ("v_%d"%x, "\\sqrt{y_%d}"%x)
|
|
]
|
|
vs.append(v)
|
|
sqrt_ys.append(sqrt_y)
|
|
start, end = [
|
|
Mobject(
|
|
left.copy(), mobs[0], equals.copy(), right.copy(), mobs[1]
|
|
).replace(frame)
|
|
for mobs in (vs, sqrt_ys)
|
|
]
|
|
|
|
self.add(start)
|
|
self.wait(2)
|
|
self.play(Transform(
|
|
start, end,
|
|
path_func = counterclockwise_path()
|
|
))
|
|
self.wait(2)
|
|
self.remove(start, end)
|
|
|
|
def show_main_equation(self):
|
|
self.equation = TexMobject("""
|
|
\\dfrac{\\sin(\\theta)}{\\sqrt{y}} =
|
|
\\text{constant}
|
|
""")
|
|
self.equation.shift(LEFT)
|
|
self.equation.shift(
|
|
(self.layer_tops[0]-self.equation.get_top())*UP
|
|
)
|
|
self.add(self.equation)
|
|
self.wait()
|
|
|
|
def ask_continuous_question(self):
|
|
continuous = self.get_continuous_background()
|
|
line = Line(
|
|
UP, DOWN,
|
|
density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D
|
|
)
|
|
theta = TexMobject("\\theta")
|
|
theta.scale(0.5/self.zoom_factor)
|
|
|
|
self.play(
|
|
ShowCreation(continuous),
|
|
Animation(self.equation)
|
|
)
|
|
self.remove(*self.layers)
|
|
self.play(ShowCreation(self.cycloid))
|
|
self.activate_zooming()
|
|
little_square = self.get_zoomed_camera_mobject()
|
|
|
|
self.add(line)
|
|
indices = np.arange(
|
|
0, self.cycloid.get_num_points()-1, 10
|
|
)
|
|
for index in indices:
|
|
point = self.cycloid.points[index]
|
|
next_point = self.cycloid.points[index+1]
|
|
angle = angle_of_vector(point - next_point)
|
|
for mob in little_square, line:
|
|
mob.shift(point - mob.get_center())
|
|
arc = Arc(angle-np.pi/2, start_angle = np.pi/2)
|
|
arc.scale(0.1)
|
|
arc.shift(point)
|
|
self.add(arc)
|
|
if angle > np.pi/2 + np.pi/6:
|
|
vect_angle = interpolate(np.pi/2, angle, 0.5)
|
|
vect = rotate_vector(RIGHT, vect_angle)
|
|
theta.center()
|
|
theta.shift(point)
|
|
theta.shift(0.15*vect)
|
|
self.add(theta)
|
|
self.wait(self.frame_duration)
|
|
self.remove(arc) |