mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 13:34:19 +08:00
Starting redo of Brachistochrone
This commit is contained in:
@ -68,8 +68,6 @@ class ShowPassingFlash(ShowPartial):
|
|||||||
return (lower, upper)
|
return (lower, upper)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Flash(Animation):
|
class Flash(Animation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"color" : "white",
|
"color" : "white",
|
||||||
|
@ -6,7 +6,7 @@ from helpers import *
|
|||||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
ImageMobject, MobjectFromPixelArray
|
||||||
from topics.three_dimensions import Stars
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
from animation import Animation
|
from animation import Animation
|
||||||
|
@ -14,7 +14,7 @@ from helpers import *
|
|||||||
from mobject.tex_mobject import TexMobject
|
from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
ImageMobject, MobjectFromPixelArray
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject
|
from mobject.tex_mobject import TextMobject, TexMobject
|
||||||
|
|
||||||
from animation.transform import \
|
from animation.transform import \
|
||||||
|
@ -6,7 +6,7 @@ from helpers import *
|
|||||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
from mobject import Mobject, Mobject1D
|
from mobject import Mobject, Mobject1D
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
ImageMobject, MobjectFromPixelArray
|
||||||
from topics.three_dimensions import Stars
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
from animation import Animation
|
from animation import Animation
|
||||||
@ -26,6 +26,7 @@ from topics.functions import ParametricFunction, FunctionGraph
|
|||||||
from topics.number_line import NumberPlane
|
from topics.number_line import NumberPlane
|
||||||
from mobject.region import Region, region_from_polygon_vertices
|
from mobject.region import Region, region_from_polygon_vertices
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
|
from scene.zoomed_scene import ZoomedScene
|
||||||
|
|
||||||
from brachistochrone.curves import Cycloid
|
from brachistochrone.curves import Cycloid
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ class MultipathPhotonScene(PhotonScene):
|
|||||||
CONFIG = {
|
CONFIG = {
|
||||||
"num_paths" : 5
|
"num_paths" : 5
|
||||||
}
|
}
|
||||||
def run_along_paths(self):
|
def run_along_paths(self, **kwargs):
|
||||||
paths = self.get_paths()
|
paths = self.get_paths()
|
||||||
colors = Color(YELLOW).range_to(WHITE, len(paths))
|
colors = Color(YELLOW).range_to(WHITE, len(paths))
|
||||||
for path, color in zip(paths, colors):
|
for path, color in zip(paths, colors):
|
||||||
@ -104,7 +105,8 @@ class MultipathPhotonScene(PhotonScene):
|
|||||||
ShowCreation(
|
ShowCreation(
|
||||||
path,
|
path,
|
||||||
rate_func = lambda t : 0.9*smooth(t)
|
rate_func = lambda t : 0.9*smooth(t)
|
||||||
)
|
),
|
||||||
|
**kwargs
|
||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
@ -306,64 +308,59 @@ class ShowMultiplePathsInGlass(ShowMultiplePathsScene):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class MultilayeredGlass(PhotonScene):
|
class MultilayeredGlass(PhotonScene, ZoomedScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"num_discrete_layers" : 5,
|
"num_discrete_layers" : 5,
|
||||||
"num_variables" : 3,
|
"num_variables" : 3,
|
||||||
"top_color" : BLUE_E,
|
"top_color" : BLUE_E,
|
||||||
"bottom_color" : BLUE_A,
|
"bottom_color" : BLUE_A,
|
||||||
|
"zoomed_canvas_space_shape" : (5, 5),
|
||||||
|
"square_color" : GREEN_B,
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.cycloid = Cycloid(end_theta = np.pi)
|
self.cycloid = Cycloid(end_theta = np.pi)
|
||||||
|
self.cycloid.highlight(YELLOW)
|
||||||
self.top = self.cycloid.get_top()[1]
|
self.top = self.cycloid.get_top()[1]
|
||||||
self.bottom = self.cycloid.get_bottom()[1]-1
|
self.bottom = self.cycloid.get_bottom()[1]-1
|
||||||
self.generate_layer_regions()
|
self.generate_layers()
|
||||||
self.generate_discrete_path()
|
self.generate_discrete_path()
|
||||||
photon_run = self.photon_run_along_path(
|
photon_run = self.photon_run_along_path(
|
||||||
self.augmented_path,
|
self.discrete_path,
|
||||||
run_time = 1,
|
run_time = 1,
|
||||||
rate_func = rush_into
|
rate_func = rush_into
|
||||||
)
|
)
|
||||||
|
|
||||||
# self.continuous_to_smooth()
|
self.continuous_to_smooth()
|
||||||
self.paint_layers()
|
self.add(*self.layers)
|
||||||
self.show_layer_variables()
|
self.show_layer_variables()
|
||||||
self.play(photon_run)
|
self.play(photon_run)
|
||||||
self.play(ShowCreation(self.discrete_path))
|
self.play(ShowCreation(self.discrete_path))
|
||||||
self.isolate_bend_points()
|
self.isolate_bend_points()
|
||||||
# self.dither()
|
self.clear()
|
||||||
|
self.add(*self.layers)
|
||||||
|
self.show_main_equation()
|
||||||
|
self.ask_continuous_question()
|
||||||
|
|
||||||
def continuous_to_smooth(self):
|
def continuous_to_smooth(self):
|
||||||
|
self.add(*self.layers)
|
||||||
continuous = self.get_continuous_background()
|
continuous = self.get_continuous_background()
|
||||||
layers = Mobject(*[
|
self.add(continuous)
|
||||||
MobjectFromRegion(region, color)
|
|
||||||
for region, color in zip(
|
|
||||||
self.layer_regions, self.layer_colors
|
|
||||||
)
|
|
||||||
])
|
|
||||||
layers.ingest_sub_mobjects()
|
|
||||||
|
|
||||||
self.play(FadeIn(continuous))
|
|
||||||
self.play(Transform(continuous, layers))
|
|
||||||
self.remove(continuous)
|
|
||||||
self.paint_layers()
|
|
||||||
self.dither()
|
self.dither()
|
||||||
|
self.play(ShowCreation(
|
||||||
def paint_layers(self):
|
continuous,
|
||||||
# for region, color in zip(self.layer_regions, self.layer_colors):
|
rate_func = lambda t : smooth(1-t)
|
||||||
# self.highlight_region(region, color)
|
|
||||||
for top, color in zip(self.layer_tops, self.layer_colors):
|
|
||||||
self.add(Line(
|
|
||||||
SPACE_WIDTH*LEFT+top*UP, SPACE_WIDTH*RIGHT+top*UP,
|
|
||||||
color = color
|
|
||||||
))
|
|
||||||
|
|
||||||
def get_continuous_background(self):
|
|
||||||
glass = MobjectFromRegion(Region(
|
|
||||||
lambda x, y : (y < self.top) & (y > self.bottom)
|
|
||||||
))
|
))
|
||||||
|
self.remove(continuous)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def get_continuous_background(self):
|
||||||
|
glass = FilledRectangle(
|
||||||
|
height = self.top-self.bottom,
|
||||||
|
width = 2*SPACE_WIDTH,
|
||||||
|
)
|
||||||
|
glass.sort_points(lambda p : -p[1])
|
||||||
|
glass.shift((self.top-glass.get_top()[1])*UP)
|
||||||
glass.gradient_highlight(self.top_color, self.bottom_color)
|
glass.gradient_highlight(self.top_color, self.bottom_color)
|
||||||
glass.scale_in_place(0.99)
|
|
||||||
return glass
|
return glass
|
||||||
|
|
||||||
def generate_layer_info(self):
|
def generate_layer_info(self):
|
||||||
@ -381,27 +378,42 @@ class MultilayeredGlass(PhotonScene):
|
|||||||
for alpha in np.arange(0, 1+epsilon, epsilon)
|
for alpha in np.arange(0, 1+epsilon, epsilon)
|
||||||
]
|
]
|
||||||
|
|
||||||
def generate_layer_regions(self):
|
def generate_layers(self):
|
||||||
self.generate_layer_info()
|
self.generate_layer_info()
|
||||||
self.layer_regions = [
|
def create_region(top, color):
|
||||||
Region(lambda x, y : (y < top) & (y > top-self.layer_thickness))
|
return Region(
|
||||||
for top in self.layer_tops
|
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):
|
def generate_discrete_path(self):
|
||||||
points = self.cycloid.points
|
points = self.cycloid.points
|
||||||
|
tops = list(self.layer_tops)
|
||||||
|
tops.append(tops[-1]-self.layer_thickness)
|
||||||
indices = [
|
indices = [
|
||||||
np.argmin(np.abs(points[:, 1]-top))
|
np.argmin(np.abs(points[:, 1]-top))
|
||||||
for top in self.layer_tops
|
for top in tops
|
||||||
]
|
]
|
||||||
self.bend_points = points[indices[1:-1]]
|
self.bend_points = points[indices[1:-1]]
|
||||||
self.discrete_path = Mobject1D(color = YELLOW)
|
self.path_angles = []
|
||||||
|
self.discrete_path = Mobject1D(
|
||||||
|
color = YELLOW,
|
||||||
|
density = 3*DEFAULT_POINT_DENSITY_1D
|
||||||
|
)
|
||||||
for start, end in zip(indices, indices[1:]):
|
for start, end in zip(indices, indices[1:]):
|
||||||
|
start_point, end_point = points[start], points[end]
|
||||||
self.discrete_path.add_line(
|
self.discrete_path.add_line(
|
||||||
points[start], points[end]
|
start_point, end_point
|
||||||
)
|
)
|
||||||
self.augmented_path = self.discrete_path.copy()
|
self.path_angles.append(
|
||||||
self.augmented_path.add_line(
|
angle_of_vector(start_point-end_point)-np.pi/2
|
||||||
|
)
|
||||||
|
self.discrete_path.add_line(
|
||||||
points[end], SPACE_WIDTH*RIGHT+(self.layer_tops[-1]-1)*UP
|
points[end], SPACE_WIDTH*RIGHT+(self.layer_tops[-1]-1)*UP
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -443,6 +455,7 @@ class MultilayeredGlass(PhotonScene):
|
|||||||
start_ys.append(start_y)
|
start_ys.append(start_y)
|
||||||
end_ys.append(end_y)
|
end_ys.append(end_y)
|
||||||
braces.append(brace)
|
braces.append(brace)
|
||||||
|
|
||||||
for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]):
|
for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]):
|
||||||
photon_run = self.photon_run_along_path(
|
photon_run = self.photon_run_along_path(
|
||||||
path,
|
path,
|
||||||
@ -455,7 +468,6 @@ class MultilayeredGlass(PhotonScene):
|
|||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
for start_y, brace in zip(start_ys, braces):
|
for start_y, brace in zip(start_ys, braces):
|
||||||
start_y.highlight(BLACK)
|
|
||||||
self.add(start_y)
|
self.add(start_y)
|
||||||
self.play(GrowFromCenter(brace))
|
self.play(GrowFromCenter(brace))
|
||||||
self.dither()
|
self.dither()
|
||||||
@ -473,49 +485,184 @@ class MultilayeredGlass(PhotonScene):
|
|||||||
self.equations.append(Mobject(*v_eq))
|
self.equations.append(Mobject(*v_eq))
|
||||||
|
|
||||||
def isolate_bend_points(self):
|
def isolate_bend_points(self):
|
||||||
little_square = Square(side_length = 4, color = WHITE)
|
arc_radius = 0.1
|
||||||
little_square.scale(0.25)
|
self.activate_zooming()
|
||||||
little_square.shift(self.bend_points[0])
|
little_square = self.get_zoomed_camera_mobject()
|
||||||
big_square = little_square.copy()
|
|
||||||
big_square.scale(4)
|
|
||||||
big_square.to_corner(UP+RIGHT)
|
|
||||||
|
|
||||||
|
for index in range(3):
|
||||||
first_time = True
|
bend_point = self.bend_points[index]
|
||||||
for bend_point in self.bend_points:
|
line = Line(
|
||||||
if first_time:
|
bend_point+DOWN,
|
||||||
self.play(ShowCreation(little_square))
|
bend_point+UP,
|
||||||
first_time = False
|
color = WHITE,
|
||||||
else:
|
density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D
|
||||||
self.remove(lines, big_square)
|
|
||||||
self.play(ApplyMethod(
|
|
||||||
little_square.shift,
|
|
||||||
bend_point - little_square.get_center()
|
|
||||||
))
|
|
||||||
lines = self.lines_connecting_squares(little_square, big_square)
|
|
||||||
self.play(
|
|
||||||
ShowCreation(lines),
|
|
||||||
ShowCreation(big_square)
|
|
||||||
)
|
)
|
||||||
self.dither(2)
|
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,
|
||||||
def lines_connecting_squares(self, square1, square2):
|
bend_point - little_square.get_center(),
|
||||||
return Mobject(*[
|
run_time = 2
|
||||||
Line(
|
))
|
||||||
square1.get_corner(vect),
|
self.play(*map(ShowCreation, figure_marks))
|
||||||
square2.get_corner(vect),
|
self.dither()
|
||||||
|
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)
|
||||||
)
|
)
|
||||||
for vect in [UP+LEFT, DOWN+LEFT]
|
equation_frame.scale_in_place(0.9)
|
||||||
]).highlight(square1.get_color())
|
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.dither(2)
|
||||||
|
self.play(Transform(
|
||||||
|
start, end,
|
||||||
|
path_func = counterclockwise_path()
|
||||||
|
))
|
||||||
|
self.dither(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.dither()
|
||||||
|
|
||||||
|
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.dither(self.frame_duration)
|
||||||
|
self.remove(arc)
|
||||||
|
|
||||||
|
|
||||||
|
class StraightLinesFastestInConstantMedium(PhotonScene):
|
||||||
|
def construct(self):
|
||||||
|
kwargs = {"size" : "\\Large"}
|
||||||
|
left = TextMobject("Speed of light is constant", **kwargs)
|
||||||
|
arrow = TexMobject("\\Rightarrow", **kwargs)
|
||||||
|
right = TextMobject("Staight path is fastest", **kwargs)
|
||||||
|
left.next_to(arrow, LEFT)
|
||||||
|
right.next_to(arrow, RIGHT)
|
||||||
|
squaggle, line = self.get_paths()
|
||||||
|
|
||||||
|
self.play(*map(ShimmerIn, [left, arrow, right]))
|
||||||
|
self.play(ShowCreation(squaggle))
|
||||||
|
self.play(Transform(
|
||||||
|
squaggle, line,
|
||||||
|
path_func = path_along_arc(np.pi)
|
||||||
|
))
|
||||||
|
self.play(self.photon_run_along_path(line))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
def get_paths(self):
|
||||||
class MultilayeredGlassZoomIn(Scene):
|
squaggle = ParametricFunction(
|
||||||
def construct(self, layer_number):
|
lambda t : (0.5*t+np.cos(t))*RIGHT+np.sin(t)*UP,
|
||||||
|
start = -np.pi,
|
||||||
|
end = 2*np.pi
|
||||||
|
)
|
||||||
|
squaggle.shift(2*UP)
|
||||||
|
start, end = squaggle.points[0], squaggle.points[-1]
|
||||||
|
line = Line(start, end)
|
||||||
|
result = [squaggle, line]
|
||||||
|
for mob in result:
|
||||||
|
mob.highlight(BLUE_D)
|
||||||
|
return result
|
||||||
|
|
||||||
|
class GlassAndAir(PhotonScene):
|
||||||
|
def construct(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -526,3 +673,5 @@ class MultilayeredGlassZoomIn(Scene):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
import numpy as np
|
|
||||||
import itertools as it
|
|
||||||
|
|
||||||
from helpers import *
|
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
|
||||||
from mobject import Mobject
|
|
||||||
from mobject.image_mobject import \
|
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
|
||||||
from topics.three_dimensions import Stars
|
|
||||||
|
|
||||||
from animation import Animation
|
|
||||||
from animation.transform import \
|
|
||||||
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
|
||||||
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
|
||||||
ShimmerIn
|
|
||||||
from animation.simple_animations import \
|
|
||||||
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
|
||||||
ShowPassingFlash
|
|
||||||
from animation.playground import TurnInsideOut, Vibrate
|
|
||||||
from topics.geometry import \
|
|
||||||
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
|
||||||
Arc, FilledRectangle
|
|
||||||
from topics.characters import Randolph, Mathematician
|
|
||||||
from topics.functions import ParametricFunction, FunctionGraph
|
|
||||||
from topics.number_line import NumberPlane
|
|
||||||
from mobject.region import Region, region_from_polygon_vertices
|
|
||||||
from scene import Scene
|
|
||||||
|
|
||||||
|
|
||||||
class OceanScene(Scene):
|
|
||||||
def construct(self):
|
|
||||||
self.rolling_waves()
|
|
||||||
|
|
||||||
def rolling_waves(self):
|
|
||||||
if not hasattr(self, "ocean"):
|
|
||||||
self.setup_ocean()
|
|
||||||
for state in self.ocean_states:
|
|
||||||
self.play(Transform(self.ocean, state))
|
|
||||||
|
|
||||||
|
|
||||||
def setup_ocean(self):
|
|
||||||
def func(points):
|
|
||||||
result = np.zeros(points.shape)
|
|
||||||
result[:,1] = 0.25 * np.sin(points[:,0]) * np.sin(points[:,1])
|
|
||||||
return result
|
|
||||||
|
|
||||||
self.ocean_states = []
|
|
||||||
for unit in -1, 1:
|
|
||||||
ocean = FilledRectangle(
|
|
||||||
color = BLUE_D,
|
|
||||||
density = 25
|
|
||||||
)
|
|
||||||
nudges = unit*func(ocean.points)
|
|
||||||
ocean.points += nudges
|
|
||||||
alphas = nudges[:,1]
|
|
||||||
alphas -= np.min(alphas)
|
|
||||||
whites = np.ones(ocean.rgbs.shape)
|
|
||||||
thick_alphas = alphas.repeat(3).reshape((len(alphas), 3))
|
|
||||||
ocean.rgbs = interpolate(ocean.rgbs, whites, thick_alphas)
|
|
||||||
self.ocean_states.append(ocean)
|
|
||||||
self.ocean = self.ocean_states[1].copy()
|
|
83
brachistochrone/misc.py
Normal file
83
brachistochrone/misc.py
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import numpy as np
|
||||||
|
import itertools as it
|
||||||
|
|
||||||
|
from helpers import *
|
||||||
|
|
||||||
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.image_mobject import ImageMobject
|
||||||
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
|
from animation import Animation
|
||||||
|
from animation.transform import \
|
||||||
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
|
FadeIn, FadeOut, GrowFromCenter, ApplyFunction, ApplyMethod, \
|
||||||
|
ShimmerIn
|
||||||
|
from animation.simple_animations import \
|
||||||
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder, \
|
||||||
|
ShowPassingFlash
|
||||||
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
|
from topics.geometry import \
|
||||||
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point, \
|
||||||
|
Arc, FilledRectangle
|
||||||
|
from topics.characters import Randolph, Mathematician
|
||||||
|
from topics.functions import ParametricFunction, FunctionGraph
|
||||||
|
from topics.number_line import NumberLine, NumberPlane
|
||||||
|
from mobject.region import Region, region_from_polygon_vertices
|
||||||
|
from scene import Scene
|
||||||
|
|
||||||
|
|
||||||
|
class PhysicalIntuition(Scene):
|
||||||
|
def construct(self):
|
||||||
|
n_terms = 4
|
||||||
|
def func((x, y, ignore)):
|
||||||
|
z = complex(x, y)
|
||||||
|
if (np.abs(x%1 - 0.5)<0.01 and y < 0.01) or np.abs(z)<0.01:
|
||||||
|
return ORIGIN
|
||||||
|
out_z = 1./(2*np.tan(np.pi*z)*(z**2))
|
||||||
|
return out_z.real*RIGHT - out_z.imag*UP
|
||||||
|
arrows = Mobject(*[
|
||||||
|
Arrow(ORIGIN, np.sqrt(2)*point)
|
||||||
|
for point in compass_directions(4, RIGHT+UP)
|
||||||
|
])
|
||||||
|
arrows.highlight(YELLOW)
|
||||||
|
arrows.ingest_sub_mobjects()
|
||||||
|
all_arrows = Mobject(*[
|
||||||
|
arrows.copy().scale(0.3/(x)).shift(x*RIGHT)
|
||||||
|
for x in range(1, n_terms+2)
|
||||||
|
])
|
||||||
|
terms = TexMobject([
|
||||||
|
"\\dfrac{1}{%d^2} + "%(x+1)
|
||||||
|
for x in range(n_terms)
|
||||||
|
]+["\\cdots"])
|
||||||
|
terms.shift(2*UP)
|
||||||
|
plane = NumberPlane(color = BLUE_E)
|
||||||
|
axes = Mobject(NumberLine(), NumberLine().rotate(np.pi/2))
|
||||||
|
axes.highlight(WHITE)
|
||||||
|
|
||||||
|
for term in terms.split():
|
||||||
|
self.play(ShimmerIn(term, run_time = 0.5))
|
||||||
|
self.dither()
|
||||||
|
self.play(ShowCreation(plane), ShowCreation(axes))
|
||||||
|
self.play(*[
|
||||||
|
Transform(*pair)
|
||||||
|
for pair in zip(terms.split(), all_arrows.split())
|
||||||
|
])
|
||||||
|
self.play(PhaseFlow(
|
||||||
|
func, plane,
|
||||||
|
run_time = 5,
|
||||||
|
virtual_time = 8
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,7 +7,7 @@ from helpers import *
|
|||||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
ImageMobject, MobjectFromPixelArray
|
||||||
from topics.three_dimensions import Stars
|
from topics.three_dimensions import Stars
|
||||||
|
|
||||||
from animation import Animation
|
from animation import Animation
|
||||||
@ -100,17 +100,43 @@ class IntroduceSteve(Scene):
|
|||||||
cornell.replace(sample_size)
|
cornell.replace(sample_size)
|
||||||
cornell.next_to(name, DOWN)
|
cornell.next_to(name, DOWN)
|
||||||
|
|
||||||
Mobject(*logos+books+[cornell]).show()
|
|
||||||
|
|
||||||
self.add(name)
|
self.add(name)
|
||||||
self.play(FadeIn(cornell))
|
self.play(FadeIn(cornell))
|
||||||
self.play(ShimmerIn(contributions))
|
|
||||||
for logo in logos:
|
|
||||||
self.play(FadeIn(logo))
|
|
||||||
self.play(ShimmerIn(books_word))
|
self.play(ShimmerIn(books_word))
|
||||||
for book in books:
|
for book in books:
|
||||||
book.shift(5*LEFT)
|
book.shift(5*LEFT)
|
||||||
self.play(ApplyMethod(book.shift, 5*RIGHT))
|
self.play(ApplyMethod(book.shift, 5*RIGHT))
|
||||||
|
self.play(ShimmerIn(contributions))
|
||||||
|
for logo in logos:
|
||||||
|
self.play(FadeIn(logo))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class ShowTweets(Scene):
|
||||||
|
def construct(self):
|
||||||
|
tweets = [
|
||||||
|
ImageMobject("tweet%d"%x, invert = False)
|
||||||
|
for x in range(1, 4)
|
||||||
|
]
|
||||||
|
for tweet in tweets:
|
||||||
|
tweet.scale(0.4)
|
||||||
|
tweets[0].to_corner(UP+LEFT)
|
||||||
|
tweets[1].next_to(tweets[0], RIGHT, aligned_edge = UP)
|
||||||
|
tweets[2].next_to(tweets[1], DOWN)
|
||||||
|
|
||||||
|
self.play(GrowFromCenter(tweets[0]))
|
||||||
|
for x in 1, 2:
|
||||||
|
self.play(
|
||||||
|
Transform(Point(tweets[x-1].get_center()), tweets[x]),
|
||||||
|
Animation(tweets[x-1])
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class LetsBeHonest(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.play(ShimmerIn(TextMobject("""
|
||||||
|
Let's be honest about who benefits
|
||||||
|
from this collaboration...
|
||||||
|
""")))
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
@ -201,6 +227,85 @@ class DisectBrachistochroneWord(Scene):
|
|||||||
)
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
class OneSolutionTwoInsights(Scene):
|
||||||
|
def construct(self):
|
||||||
|
one_solution = TextMobject(["One ", "solution"])
|
||||||
|
two_insights = TextMobject(["Two ", " insights"])
|
||||||
|
two, insights = two_insights.split()
|
||||||
|
johann = ImageMobject("Johann_Bernoulli2", invert = False)
|
||||||
|
mark = ImageMobject("Mark_Levi", invert = False)
|
||||||
|
for mob in johann, mark:
|
||||||
|
mob.scale(0.4)
|
||||||
|
johann.next_to(insights, LEFT)
|
||||||
|
mark.next_to(johann, RIGHT)
|
||||||
|
name = TextMobject("Mark Levi").to_edge(UP)
|
||||||
|
|
||||||
|
self.play(*map(ShimmerIn, one_solution.split()))
|
||||||
|
self.dither()
|
||||||
|
for pair in zip(one_solution.split(), two_insights.split()):
|
||||||
|
self.play(Transform(*pair, path_func = path_along_arc(np.pi)))
|
||||||
|
self.dither()
|
||||||
|
self.clear()
|
||||||
|
self.add(two, insights)
|
||||||
|
for word, man in [(two, johann), (insights, mark)]:
|
||||||
|
self.play(
|
||||||
|
Transform(word, Point(word.get_left())),
|
||||||
|
GrowFromCenter(man)
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.clear()
|
||||||
|
self.play(ApplyMethod(mark.center))
|
||||||
|
self.play(ShimmerIn(name))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class CircleOfIdeas(Scene):
|
||||||
|
def construct(self):
|
||||||
|
words = map(TextMobject, [
|
||||||
|
"optics", "calculus", "mechanics", "geometry", "history"
|
||||||
|
])
|
||||||
|
words[0].highlight(YELLOW)
|
||||||
|
words[1].highlight(BLUE_D)
|
||||||
|
words[2].highlight(GREY)
|
||||||
|
words[3].highlight(GREEN)
|
||||||
|
words[4].highlight(MAROON)
|
||||||
|
brachistochrone = TextMobject("Brachistochrone")
|
||||||
|
displayed_words = []
|
||||||
|
for word in words:
|
||||||
|
anims = self.get_spinning_anims(displayed_words)
|
||||||
|
word.shift(3*RIGHT)
|
||||||
|
point = Point()
|
||||||
|
anims.append(Transform(point, word))
|
||||||
|
self.play(*anims)
|
||||||
|
self.remove(point)
|
||||||
|
self.add(word)
|
||||||
|
displayed_words.append(word)
|
||||||
|
self.play(*self.get_spinning_anims(displayed_words))
|
||||||
|
self.play(*[
|
||||||
|
Transform(
|
||||||
|
word, word.copy().highlight(BLACK).center().scale(0.1),
|
||||||
|
path_func = path_along_arc(np.pi),
|
||||||
|
rate_func = None,
|
||||||
|
run_time = 2
|
||||||
|
)
|
||||||
|
for word in displayed_words
|
||||||
|
]+[
|
||||||
|
GrowFromCenter(brachistochrone)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def get_spinning_anims(self, words, angle = np.pi/6):
|
||||||
|
anims = []
|
||||||
|
for word in words:
|
||||||
|
old_center = word.get_center()
|
||||||
|
new_center = rotate_vector(old_center, angle)
|
||||||
|
vect = new_center-old_center
|
||||||
|
anims.append(ApplyMethod(
|
||||||
|
word.shift, vect,
|
||||||
|
path_func = path_along_arc(angle),
|
||||||
|
rate_func = None
|
||||||
|
))
|
||||||
|
return anims
|
||||||
|
|
||||||
|
|
||||||
class FermatsPrincipleStatement(Scene):
|
class FermatsPrincipleStatement(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
@ -299,6 +404,10 @@ class VideoProgression(Scene):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BalanceCompetingFactors(Scene):
|
||||||
|
def construct(self):
|
||||||
|
factor1 = TextMobject("Factor 1")
|
||||||
|
factor2 = TextMobject("Factor 2")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -151,10 +151,9 @@ class Camera(object):
|
|||||||
])
|
])
|
||||||
|
|
||||||
def adjusted_thickness(self, thickness):
|
def adjusted_thickness(self, thickness):
|
||||||
# big_shape = PRODUCTION_QUALITY_DISPLAY_CONFIG["pixel_shape"]
|
big_shape = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_shape"]
|
||||||
# factor = sum(big_shape)/sum(self.pixel_shape)
|
factor = sum(big_shape)/sum(self.pixel_shape)
|
||||||
# return 1 + (thickness-1)/factor
|
return 1 + (thickness-1)/factor
|
||||||
return thickness
|
|
||||||
|
|
||||||
def get_thickening_nudges(self, thickness):
|
def get_thickening_nudges(self, thickness):
|
||||||
_range = range(-thickness/2+1, thickness/2+1)
|
_range = range(-thickness/2+1, thickness/2+1)
|
||||||
|
@ -6,15 +6,15 @@ DEFAULT_WIDTH = 2560
|
|||||||
DEFAULT_FRAME_DURATION = 0.04
|
DEFAULT_FRAME_DURATION = 0.04
|
||||||
|
|
||||||
#There might be other configuration than pixel_shape later...
|
#There might be other configuration than pixel_shape later...
|
||||||
PRODUCTION_QUALITY_DISPLAY_CONFIG = {
|
PRODUCTION_QUALITY_CAMERA_CONFIG = {
|
||||||
"pixel_shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH),
|
"pixel_shape" : (DEFAULT_HEIGHT, DEFAULT_WIDTH),
|
||||||
}
|
}
|
||||||
|
|
||||||
MEDIUM_QUALITY_DISPLAY_CONFIG = {
|
MEDIUM_QUALITY_CAMERA_CONFIG = {
|
||||||
"pixel_shape" : (720, 1280),
|
"pixel_shape" : (720, 1280),
|
||||||
}
|
}
|
||||||
|
|
||||||
LOW_QUALITY_DISPLAY_CONFIG = {
|
LOW_QUALITY_CAMERA_CONFIG = {
|
||||||
"pixel_shape" : (576, 1024),
|
"pixel_shape" : (576, 1024),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ def get_configuration(sys_argv):
|
|||||||
"file" : None,
|
"file" : None,
|
||||||
"scene_name" : "",
|
"scene_name" : "",
|
||||||
"args_extension" : "",
|
"args_extension" : "",
|
||||||
"display_config" : PRODUCTION_QUALITY_DISPLAY_CONFIG,
|
"camera_config" : PRODUCTION_QUALITY_CAMERA_CONFIG,
|
||||||
"preview" : False,
|
"preview" : False,
|
||||||
"write" : False,
|
"write" : False,
|
||||||
"save_image" : False,
|
"save_image" : False,
|
||||||
@ -61,11 +61,11 @@ def get_configuration(sys_argv):
|
|||||||
print HELP_MESSAGE
|
print HELP_MESSAGE
|
||||||
return
|
return
|
||||||
elif opt == '-l':
|
elif opt == '-l':
|
||||||
config["display_config"] = LOW_QUALITY_DISPLAY_CONFIG
|
config["camera_config"] = LOW_QUALITY_CAMERA_CONFIG
|
||||||
elif opt == '-m':
|
elif opt == '-m':
|
||||||
config["display_config"] = MEDIUM_QUALITY_DISPLAY_CONFIG
|
config["camera_config"] = MEDIUM_QUALITY_CAMERA_CONFIG
|
||||||
elif opt == '-p':
|
elif opt == '-p':
|
||||||
config["display_config"] = LOW_QUALITY_DISPLAY_CONFIG
|
config["camera_config"] = LOW_QUALITY_CAMERA_CONFIG
|
||||||
config["preview"] = True
|
config["preview"] = True
|
||||||
elif opt == '-w':
|
elif opt == '-w':
|
||||||
config["write"] = True
|
config["write"] = True
|
||||||
@ -196,7 +196,7 @@ def main():
|
|||||||
)
|
)
|
||||||
config["movie_prefix"] = config["file"].replace(".py", "")
|
config["movie_prefix"] = config["file"].replace(".py", "")
|
||||||
scene_kwargs = {
|
scene_kwargs = {
|
||||||
"camera" : Camera(**config["display_config"])
|
"camera_config" : config["camera_config"]
|
||||||
}
|
}
|
||||||
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
for SceneClass in get_scene_classes(scene_names_to_classes, config):
|
||||||
for args in get_scene_args(SceneClass, config):
|
for args in get_scene_args(SceneClass, config):
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import MobjectFromRegion
|
|
||||||
from mobject.tex_mobject import TextMobject
|
from mobject.tex_mobject import TextMobject
|
||||||
from mobject.region import region_from_polygon_vertices
|
from mobject.region import region_from_polygon_vertices
|
||||||
from topics.geometry import Arrow, Dot, Circle, Line, FilledRectangle
|
from topics.geometry import Arrow, Dot, Circle, Line, FilledRectangle
|
||||||
@ -21,6 +20,7 @@ class FluidFlow(Scene):
|
|||||||
"dot_color" : BLUE_C,
|
"dot_color" : BLUE_C,
|
||||||
"text_color" : WHITE,
|
"text_color" : WHITE,
|
||||||
"arrow_color" : GREEN_A,
|
"arrow_color" : GREEN_A,
|
||||||
|
"arrow_length" : 0.5,
|
||||||
"points_height" : SPACE_HEIGHT,
|
"points_height" : SPACE_HEIGHT,
|
||||||
"points_width" : SPACE_WIDTH,
|
"points_width" : SPACE_WIDTH,
|
||||||
}
|
}
|
||||||
@ -59,7 +59,7 @@ class FluidFlow(Scene):
|
|||||||
)
|
)
|
||||||
angles = map(angle_of_vector, map(self.function, points))
|
angles = map(angle_of_vector, map(self.function, points))
|
||||||
prototype = Arrow(
|
prototype = Arrow(
|
||||||
ORIGIN, RIGHT*self.arrow_spacing/2.,
|
ORIGIN, RIGHT*self.arrow_length,
|
||||||
color = self.arrow_color,
|
color = self.arrow_color,
|
||||||
tip_length = 0.1,
|
tip_length = 0.1,
|
||||||
buff = 0
|
buff = 0
|
||||||
@ -112,6 +112,23 @@ class FluidFlow(Scene):
|
|||||||
self.remove(mob, rectangle)
|
self.remove(mob, rectangle)
|
||||||
|
|
||||||
|
|
||||||
|
class FluxArticleExample(FluidFlow):
|
||||||
|
CONFIG = {
|
||||||
|
"arrow_length" : 0.4,
|
||||||
|
"arrow_color" : BLUE_D,
|
||||||
|
"points_height" : SPACE_HEIGHT,
|
||||||
|
"points_width" : SPACE_WIDTH,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.use_function(
|
||||||
|
lambda (x, y, z) : (x**2+y**2)*((np.sin(x)**2)*RIGHT + np.cos(y)*UP)
|
||||||
|
)
|
||||||
|
# self.add_plane()
|
||||||
|
self.add_arrows()
|
||||||
|
self.show_frame()
|
||||||
|
self.add_dots()
|
||||||
|
self.flow(run_time = 2, virtual_time = 0.1)
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
class NegativeDivergenceExamlpe(FluidFlow):
|
class NegativeDivergenceExamlpe(FluidFlow):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -6,7 +6,6 @@ from random import random
|
|||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
import displayer as disp
|
|
||||||
|
|
||||||
class ImageMobject(Mobject):
|
class ImageMobject(Mobject):
|
||||||
"""
|
"""
|
||||||
@ -110,14 +109,4 @@ class MobjectFromPixelArray(ImageMobject):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class MobjectFromRegion(MobjectFromPixelArray):
|
|
||||||
def __init__(self, region, color = None, **kwargs):
|
|
||||||
MobjectFromPixelArray.__init__(
|
|
||||||
self,
|
|
||||||
disp.paint_region(region, color = color),
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ def generate_tex_file(expression, size, template_tex_file):
|
|||||||
expression = tex_expression_list_as_string(expression)
|
expression = tex_expression_list_as_string(expression)
|
||||||
result = os.path.join(TEX_DIR, tex_hash(expression, size))+".tex"
|
result = os.path.join(TEX_DIR, tex_hash(expression, size))+".tex"
|
||||||
if not os.path.exists(result):
|
if not os.path.exists(result):
|
||||||
print "Writing %s at size %s to %s"%(
|
print "Writing \"%s\" at size %s to %s"%(
|
||||||
"".join(expression), size, result
|
"".join(expression), size, result
|
||||||
)
|
)
|
||||||
with open(template_tex_file, "r") as infile:
|
with open(template_tex_file, "r") as infile:
|
||||||
|
@ -18,14 +18,13 @@ from mobject import Mobject
|
|||||||
|
|
||||||
class Scene(object):
|
class Scene(object):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera" : None,
|
"camera_config" : {},
|
||||||
"frame_duration" : DEFAULT_FRAME_DURATION,
|
"frame_duration" : DEFAULT_FRAME_DURATION,
|
||||||
"construct_args" : [],
|
"construct_args" : [],
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs)
|
digest_config(self, kwargs)
|
||||||
if not self.camera:
|
self.camera = Camera(**self.camera_config)
|
||||||
self.camera = Camera()
|
|
||||||
self.frames = []
|
self.frames = []
|
||||||
self.mobjects = []
|
self.mobjects = []
|
||||||
self.num_animations = 0
|
self.num_animations = 0
|
||||||
@ -45,12 +44,22 @@ class Scene(object):
|
|||||||
self.name = name
|
self.name = name
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
### Only these methods should touch the camera
|
||||||
|
|
||||||
def set_camera(self, camera):
|
def set_camera(self, camera):
|
||||||
self.camera = camera
|
self.camera = camera
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
return self.camera.get_image()
|
return self.camera.get_image()
|
||||||
|
|
||||||
|
def update_frame(self, mobjects, background = None, **kwargs):
|
||||||
|
if background is not None:
|
||||||
|
self.camera.set_image(background)
|
||||||
|
else:
|
||||||
|
self.camera.reset()
|
||||||
|
self.camera.capture_mobjects(mobjects, **kwargs)
|
||||||
|
###
|
||||||
|
|
||||||
def add(self, *mobjects):
|
def add(self, *mobjects):
|
||||||
"""
|
"""
|
||||||
Mobjects will be displayed, from background to foreground,
|
Mobjects will be displayed, from background to foreground,
|
||||||
@ -60,7 +69,6 @@ class Scene(object):
|
|||||||
raise Exception("Adding something which is not a mobject")
|
raise Exception("Adding something which is not a mobject")
|
||||||
old_mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
old_mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
||||||
self.mobjects = old_mobjects + list(mobjects)
|
self.mobjects = old_mobjects + list(mobjects)
|
||||||
self.camera.capture_mobjects(mobjects)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def add_mobjects_among(self, values):
|
def add_mobjects_among(self, values):
|
||||||
@ -79,7 +87,6 @@ class Scene(object):
|
|||||||
if len(mobjects) == 0:
|
if len(mobjects) == 0:
|
||||||
return
|
return
|
||||||
self.mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
self.mobjects = filter(lambda m : m not in mobjects, self.mobjects)
|
||||||
self.repaint_mojects()
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def bring_to_front(self, mobject):
|
def bring_to_front(self, mobject):
|
||||||
@ -95,11 +102,6 @@ class Scene(object):
|
|||||||
self.mobjects = []
|
self.mobjects = []
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def repaint_mojects(self):
|
|
||||||
self.camera.reset()
|
|
||||||
self.camera.capture_mobjects(self.mobjects)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def align_run_times(self, *animations, **kwargs):
|
def align_run_times(self, *animations, **kwargs):
|
||||||
if "run_time" in kwargs:
|
if "run_time" in kwargs:
|
||||||
run_time = kwargs["run_time"]
|
run_time = kwargs["run_time"]
|
||||||
@ -132,13 +134,6 @@ class Scene(object):
|
|||||||
]))
|
]))
|
||||||
return time_progression
|
return time_progression
|
||||||
|
|
||||||
def update_frame(self, moving_mobjects, static_image = None):
|
|
||||||
if static_image is not None:
|
|
||||||
self.camera.set_image(static_image)
|
|
||||||
else:
|
|
||||||
self.camera.reset()
|
|
||||||
self.camera.capture_mobjects(moving_mobjects)
|
|
||||||
|
|
||||||
|
|
||||||
def play(self, *animations, **kwargs):
|
def play(self, *animations, **kwargs):
|
||||||
if len(animations) == 0:
|
if len(animations) == 0:
|
||||||
@ -149,12 +144,11 @@ class Scene(object):
|
|||||||
animations = self.align_run_times(*animations, **kwargs)
|
animations = self.align_run_times(*animations, **kwargs)
|
||||||
moving_mobjects, static_mobjects = \
|
moving_mobjects, static_mobjects = \
|
||||||
self.separate_moving_and_static_mobjects(*animations)
|
self.separate_moving_and_static_mobjects(*animations)
|
||||||
self.camera.reset()
|
self.update_frame(
|
||||||
self.camera.capture_mobjects(
|
|
||||||
static_mobjects,
|
static_mobjects,
|
||||||
include_sub_mobjects = False
|
include_sub_mobjects = False
|
||||||
)
|
)
|
||||||
static_image = self.camera.get_image()
|
static_image = self.get_frame()
|
||||||
|
|
||||||
for t in self.get_time_progression(animations):
|
for t in self.get_time_progression(animations):
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
@ -183,15 +177,15 @@ class Scene(object):
|
|||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.update((t-t0)/(t1 - t0))
|
animation.update((t-t0)/(t1 - t0))
|
||||||
index = int(t/self.frame_duration)
|
index = int(t/self.frame_duration)
|
||||||
self.camera.set_image(self.frames[index])
|
self.update_frame(moving_mobjects, self.frames[index])
|
||||||
self.camera.capture_mobjects(moving_mobjects)
|
self.frames[index] = self.get_frame()
|
||||||
self.frames[index] = self.camera.get_image()
|
|
||||||
for animation in animations:
|
for animation in animations:
|
||||||
animation.clean_up()
|
animation.clean_up()
|
||||||
self.repaint_mojects()
|
self.repaint_mojects()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def dither(self, duration = DEFAULT_DITHER_TIME):
|
def dither(self, duration = DEFAULT_DITHER_TIME):
|
||||||
|
self.update_frame(self.mobjects)
|
||||||
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
self.frames += [self.get_frame()]*int(duration / self.frame_duration)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
58
scene/test.py
Normal file
58
scene/test.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
|
||||||
|
class CowProblem():
|
||||||
|
def __init__(self):
|
||||||
|
self.reset()
|
||||||
|
self.directions = [
|
||||||
|
(1, 0),
|
||||||
|
(1, 1),
|
||||||
|
(0, 1),
|
||||||
|
(-1, 1),
|
||||||
|
(-1, 0),
|
||||||
|
(-1, -1),
|
||||||
|
(0, -1),
|
||||||
|
(1, -1),
|
||||||
|
]
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.field = np.ones((11, 11), dtype = 'bool')
|
||||||
|
self.cow_x = 0
|
||||||
|
self.cow_y = 0
|
||||||
|
|
||||||
|
def step(self):
|
||||||
|
valid_step = False
|
||||||
|
while not valid_step:
|
||||||
|
step_x, step_y = random.choice(self.directions)
|
||||||
|
if self.cow_x + step_x > 0 and self.cow_x + step_x < 11 and self.cow_y + step_y > 0 and self.cow_y + step_y < 11:
|
||||||
|
valid_step = True
|
||||||
|
self.cow_x += step_x
|
||||||
|
self.cow_y += step_y
|
||||||
|
self.field[self.cow_x, self.cow_y] = False
|
||||||
|
|
||||||
|
def total_grass_after_n_steps(self, n):
|
||||||
|
for x in range(n):
|
||||||
|
self.step()
|
||||||
|
return sum(sum(self.field))
|
||||||
|
|
||||||
|
def num_steps_for_half_eaten(self):
|
||||||
|
result = 0
|
||||||
|
while sum(sum(self.field)) > 121/2:
|
||||||
|
self.step()
|
||||||
|
result += 1
|
||||||
|
return result
|
||||||
|
|
||||||
|
def average_number_of_steps_for_half_eaten(self, sample_size):
|
||||||
|
run_times = []
|
||||||
|
for x in range(sample_size):
|
||||||
|
run_times.append(
|
||||||
|
self.num_steps_for_half_eaten()
|
||||||
|
)
|
||||||
|
self.reset()
|
||||||
|
return np.mean(run_times)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -30,6 +30,9 @@ class ZoomedScene(Scene):
|
|||||||
def get_zoomed_camera_mobject(self):
|
def get_zoomed_camera_mobject(self):
|
||||||
return self.little_rectangle
|
return self.little_rectangle
|
||||||
|
|
||||||
|
def get_zoomed_screen(self):
|
||||||
|
return self.big_rectangle
|
||||||
|
|
||||||
def generate_big_rectangle(self):
|
def generate_big_rectangle(self):
|
||||||
height, width = self.zoomed_canvas_space_shape
|
height, width = self.zoomed_canvas_space_shape
|
||||||
self.big_rectangle = Rectangle(
|
self.big_rectangle = Rectangle(
|
||||||
@ -67,6 +70,8 @@ class ZoomedScene(Scene):
|
|||||||
background = self.zoomed_camera_background
|
background = self.zoomed_camera_background
|
||||||
)
|
)
|
||||||
self.add(self.little_rectangle)
|
self.add(self.little_rectangle)
|
||||||
|
#TODO, is there a better way to hanld this?
|
||||||
|
self.zoomed_camera.adjusted_thickness = lambda x : x
|
||||||
|
|
||||||
def get_frame(self):
|
def get_frame(self):
|
||||||
frame = Scene.get_frame(self)
|
frame = Scene.get_frame(self)
|
||||||
|
@ -246,10 +246,13 @@ class Square(Rectangle):
|
|||||||
"side_length" : 2.0,
|
"side_length" : 2.0,
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, kwargs)
|
side_length = kwargs.pop("side_length")
|
||||||
for arg in ["height", "width"]:
|
Rectangle.__init__(
|
||||||
kwargs[arg] = self.side_length
|
self,
|
||||||
Rectangle.__init__(self, **kwargs)
|
height = side_length,
|
||||||
|
width = side_length,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -262,8 +265,8 @@ class FilledRectangle(Mobject1D):
|
|||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
self.add_points([
|
self.add_points([
|
||||||
(x, y, 0)
|
(x, y, 0)
|
||||||
for x in np.arange(-self.width, self.width, self.epsilon)
|
for x in np.arange(-self.width/2, self.width/2, self.epsilon)
|
||||||
for y in np.arange(-self.height, self.height, self.epsilon)
|
for y in np.arange(-self.height/2, self.height/2, self.epsilon)
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user