mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 14:03:59 +08:00
Moving thing files around after TOP project
This commit is contained in:
@ -1,8 +1,8 @@
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
DEFAULT_HEIGHT = 1440
|
||||
DEFAULT_WIDTH = 2560
|
||||
DEFAULT_HEIGHT = 1080
|
||||
DEFAULT_WIDTH = 1920
|
||||
DEFAULT_FRAME_DURATION = 0.04
|
||||
|
||||
#There might be other configuration than pixel_shape later...
|
||||
@ -15,7 +15,7 @@ MEDIUM_QUALITY_CAMERA_CONFIG = {
|
||||
}
|
||||
|
||||
LOW_QUALITY_CAMERA_CONFIG = {
|
||||
"pixel_shape" : (576, 1024),
|
||||
"pixel_shape" : (480, 720),
|
||||
}
|
||||
|
||||
|
||||
|
@ -505,7 +505,8 @@ class Mobject(object):
|
||||
def repeat_submobject(self, submob):
|
||||
return submob.copy()
|
||||
|
||||
def interpolate(self, mobject1, mobject2, alpha, path_func):
|
||||
def interpolate(self, mobject1, mobject2,
|
||||
alpha, path_func = straight_path):
|
||||
"""
|
||||
Turns target_mobject into an interpolation between mobject1
|
||||
and mobject2.
|
||||
|
719
old_projects/brachistochrone/curves.py
Normal file
719
old_projects/brachistochrone/curves.py
Normal file
@ -0,0 +1,719 @@
|
||||
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, MobjectFromPixelArray
|
||||
from topics.three_dimensions import Stars
|
||||
|
||||
from animation import Animation
|
||||
from animation.transform import *
|
||||
from animation.simple_animations import *
|
||||
from animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import *
|
||||
from topics.characters import Randolph, Mathematician
|
||||
from topics.functions import ParametricFunction, FunctionGraph
|
||||
from topics.number_line import *
|
||||
from mobject.region import Region, region_from_polygon_vertices
|
||||
from scene import Scene
|
||||
|
||||
RANDY_SCALE_VAL = 0.3
|
||||
|
||||
|
||||
|
||||
class Cycloid(ParametricFunction):
|
||||
CONFIG = {
|
||||
"point_a" : 6*LEFT+3*UP,
|
||||
"radius" : 2,
|
||||
"end_theta" : 3*np.pi/2,
|
||||
"density" : 5*DEFAULT_POINT_DENSITY_1D,
|
||||
"color" : YELLOW
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
ParametricFunction.__init__(self, self.pos_func, **kwargs)
|
||||
|
||||
def pos_func(self, t):
|
||||
T = t*self.end_theta
|
||||
return self.point_a + self.radius * np.array([
|
||||
T - np.sin(T),
|
||||
np.cos(T) - 1,
|
||||
0
|
||||
])
|
||||
|
||||
class LoopTheLoop(ParametricFunction):
|
||||
CONFIG = {
|
||||
"color" : YELLOW_D,
|
||||
"density" : 10*DEFAULT_POINT_DENSITY_1D
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
def func(t):
|
||||
t = (6*np.pi/2)*(t-0.5)
|
||||
return (t/2-np.sin(t))*RIGHT + \
|
||||
(np.cos(t)+(t**2)/10)*UP
|
||||
ParametricFunction.__init__(self, func, **kwargs)
|
||||
|
||||
|
||||
class SlideWordDownCycloid(Animation):
|
||||
CONFIG = {
|
||||
"rate_func" : None,
|
||||
"run_time" : 8
|
||||
}
|
||||
def __init__(self, word, **kwargs):
|
||||
self.path = Cycloid(end_theta = np.pi)
|
||||
word_mob = TextMobject(list(word))
|
||||
end_word = word_mob.copy()
|
||||
end_word.shift(-end_word.get_bottom())
|
||||
end_word.shift(self.path.get_corner(DOWN+RIGHT))
|
||||
end_word.shift(3*RIGHT)
|
||||
self.end_letters = end_word.split()
|
||||
for letter in word_mob.split():
|
||||
letter.center()
|
||||
letter.angle = 0
|
||||
unit_interval = np.arange(0, 1, 1./len(word))
|
||||
self.start_times = 0.5*(1-(unit_interval))
|
||||
Animation.__init__(self, word_mob, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
virtual_times = 2*(alpha - self.start_times)
|
||||
cut_offs = [
|
||||
0.1,
|
||||
0.3,
|
||||
0.7,
|
||||
]
|
||||
for letter, time, end_letter in zip(
|
||||
self.mobject.split(), virtual_times, self.end_letters
|
||||
):
|
||||
time = max(time, 0)
|
||||
time = min(time, 1)
|
||||
if time < cut_offs[0]:
|
||||
brightness = time/cut_offs[0]
|
||||
letter.rgbs = brightness*np.ones(letter.rgbs.shape)
|
||||
position = self.path.points[0]
|
||||
angle = 0
|
||||
elif time < cut_offs[1]:
|
||||
alpha = (time-cut_offs[0])/(cut_offs[1]-cut_offs[0])
|
||||
angle = -rush_into(alpha)*np.pi/2
|
||||
position = self.path.points[0]
|
||||
elif time < cut_offs[2]:
|
||||
alpha = (time-cut_offs[1])/(cut_offs[2]-cut_offs[1])
|
||||
index = int(alpha*self.path.get_num_points())
|
||||
position = self.path.points[index]
|
||||
try:
|
||||
angle = angle_of_vector(
|
||||
self.path.points[index+1] - \
|
||||
self.path.points[index]
|
||||
)
|
||||
except:
|
||||
angle = letter.angle
|
||||
else:
|
||||
alpha = (time-cut_offs[2])/(1-cut_offs[2])
|
||||
start = self.path.points[-1]
|
||||
end = end_letter.get_bottom()
|
||||
position = interpolate(start, end, rush_from(alpha))
|
||||
angle = 0
|
||||
|
||||
letter.shift(position-letter.get_bottom())
|
||||
letter.rotate_in_place(angle-letter.angle)
|
||||
letter.angle = angle
|
||||
|
||||
|
||||
class BrachistochroneWordSliding(Scene):
|
||||
def construct(self):
|
||||
anim = SlideWordDownCycloid("Brachistochrone")
|
||||
anim.path.gradient_highlight(WHITE, BLUE_E)
|
||||
self.play(ShowCreation(anim.path))
|
||||
self.play(anim)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(anim.path),
|
||||
ApplyMethod(anim.mobject.center)
|
||||
)
|
||||
|
||||
|
||||
|
||||
class PathSlidingScene(Scene):
|
||||
CONFIG = {
|
||||
"gravity" : 3,
|
||||
"delta_t" : 0.05,
|
||||
"dither_and_add" : True,
|
||||
"show_time" : True,
|
||||
}
|
||||
def slide(self, mobject, path, roll = False, ceiling = None):
|
||||
points = path.points
|
||||
time_slices = self.get_time_slices(points, ceiling = ceiling)
|
||||
curr_t = 0
|
||||
last_index = 0
|
||||
curr_index = 1
|
||||
if self.show_time:
|
||||
self.t_equals = TexMobject("t = ")
|
||||
self.t_equals.shift(3.5*UP+4*RIGHT)
|
||||
self.add(self.t_equals)
|
||||
while curr_index < len(points):
|
||||
self.slider = mobject.copy()
|
||||
self.adjust_mobject_to_index(
|
||||
self.slider, curr_index, points
|
||||
)
|
||||
if roll:
|
||||
distance = np.linalg.norm(
|
||||
points[curr_index] - points[last_index]
|
||||
)
|
||||
self.roll(mobject, distance)
|
||||
self.add(self.slider)
|
||||
if self.show_time:
|
||||
self.write_time(curr_t)
|
||||
self.dither(self.frame_duration)
|
||||
self.remove(self.slider)
|
||||
curr_t += self.delta_t
|
||||
last_index = curr_index
|
||||
while time_slices[curr_index] < curr_t:
|
||||
curr_index += 1
|
||||
if curr_index == len(points):
|
||||
break
|
||||
if self.dither_and_add:
|
||||
self.add(self.slider)
|
||||
self.dither()
|
||||
else:
|
||||
return self.slider
|
||||
|
||||
def get_time_slices(self, points, ceiling = None):
|
||||
dt_list = np.zeros(len(points))
|
||||
ds_list = np.apply_along_axis(
|
||||
np.linalg.norm,
|
||||
1,
|
||||
points[1:]-points[:-1]
|
||||
)
|
||||
if ceiling is None:
|
||||
ceiling = points[0, 1]
|
||||
delta_y_list = np.abs(ceiling - points[1:,1])
|
||||
delta_y_list += 0.001*(delta_y_list == 0)
|
||||
v_list = self.gravity*np.sqrt(delta_y_list)
|
||||
dt_list[1:] = ds_list / v_list
|
||||
return np.cumsum(dt_list)
|
||||
|
||||
def adjust_mobject_to_index(self, mobject, index, points):
|
||||
point_a, point_b = points[index-1], points[index]
|
||||
while np.all(point_a == point_b):
|
||||
index += 1
|
||||
point_b = points[index]
|
||||
theta = angle_of_vector(point_b - point_a)
|
||||
mobject.rotate(theta)
|
||||
mobject.shift(points[index])
|
||||
self.midslide_action(point_a, theta)
|
||||
return mobject
|
||||
|
||||
def midslide_action(self, point, angle):
|
||||
pass
|
||||
|
||||
def write_time(self, time):
|
||||
if hasattr(self, "time_mob"):
|
||||
self.remove(self.time_mob)
|
||||
digits = map(TexMobject, "%.2f"%time)
|
||||
digits[0].next_to(self.t_equals, buff = 0.1)
|
||||
for left, right in zip(digits, digits[1:]):
|
||||
right.next_to(left, buff = 0.1, aligned_edge = DOWN)
|
||||
self.time_mob = Mobject(*digits)
|
||||
self.add(self.time_mob)
|
||||
|
||||
def roll(self, mobject, arc_length):
|
||||
radius = mobject.get_width()/2
|
||||
theta = arc_length / radius
|
||||
mobject.rotate_in_place(-theta)
|
||||
|
||||
def add_cycloid_end_points(self):
|
||||
cycloid = Cycloid()
|
||||
point_a = Dot(cycloid.points[0])
|
||||
point_b = Dot(cycloid.points[-1])
|
||||
A = TexMobject("A").next_to(point_a, LEFT)
|
||||
B = TexMobject("B").next_to(point_b, RIGHT)
|
||||
self.add(point_a, point_b, A, B)
|
||||
digest_locals(self)
|
||||
|
||||
|
||||
class TryManyPaths(PathSlidingScene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.shift(-randy.get_bottom())
|
||||
self.slider = randy.copy()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
paths = self.get_paths()
|
||||
point_a = Dot(paths[0].points[0])
|
||||
point_b = Dot(paths[0].points[-1])
|
||||
A = TexMobject("A").next_to(point_a, LEFT)
|
||||
B = TexMobject("B").next_to(point_b, RIGHT)
|
||||
for point, tex in [(point_a, A), (point_b, B)]:
|
||||
self.play(ShowCreation(point))
|
||||
self.play(ShimmerIn(tex))
|
||||
self.dither()
|
||||
curr_path = None
|
||||
for path in paths:
|
||||
new_slider = self.adjust_mobject_to_index(
|
||||
randy.copy(), 1, path.points
|
||||
)
|
||||
if curr_path is None:
|
||||
curr_path = path
|
||||
self.play(ShowCreation(curr_path))
|
||||
else:
|
||||
self.play(Transform(curr_path, path))
|
||||
self.play(Transform(self.slider, new_slider))
|
||||
self.dither()
|
||||
self.remove(self.slider)
|
||||
self.slide(randy, curr_path)
|
||||
self.clear()
|
||||
self.add(point_a, point_b, A, B, curr_path)
|
||||
text = self.get_text()
|
||||
text.to_edge(UP)
|
||||
self.play(ShimmerIn(text))
|
||||
for path in paths:
|
||||
self.play(Transform(
|
||||
curr_path, path,
|
||||
path_func = path_along_arc(np.pi/2),
|
||||
run_time = 3
|
||||
))
|
||||
|
||||
def get_text(self):
|
||||
return TextMobject("Which path is fastest?")
|
||||
|
||||
def get_paths(self):
|
||||
sharp_corner = Mobject(
|
||||
Line(3*UP+LEFT, LEFT),
|
||||
Arc(angle = np.pi/2, start_angle = np.pi),
|
||||
Line(DOWN, DOWN+3*RIGHT)
|
||||
).ingest_submobjects().highlight(GREEN)
|
||||
paths = [
|
||||
Arc(
|
||||
angle = np.pi/2,
|
||||
radius = 3,
|
||||
start_angle = 4
|
||||
),
|
||||
LoopTheLoop(),
|
||||
Line(7*LEFT, 7*RIGHT, color = RED_D),
|
||||
sharp_corner,
|
||||
FunctionGraph(
|
||||
lambda x : 0.05*(x**2)+0.1*np.sin(2*x)
|
||||
),
|
||||
FunctionGraph(
|
||||
lambda x : x**2,
|
||||
x_min = -3,
|
||||
x_max = 2,
|
||||
density = 3*DEFAULT_POINT_DENSITY_1D
|
||||
)
|
||||
]
|
||||
cycloid = Cycloid()
|
||||
self.align_paths(paths, cycloid)
|
||||
return paths + [cycloid]
|
||||
|
||||
def align_paths(self, paths, target_path):
|
||||
start = target_path.points[0]
|
||||
end = target_path.points[-1]
|
||||
for path in paths:
|
||||
path.position_endpoints_on(start, end)
|
||||
|
||||
|
||||
class RollingRandolph(PathSlidingScene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
self.add_cycloid_end_points()
|
||||
self.slide(randy, self.cycloid, roll = True)
|
||||
|
||||
|
||||
|
||||
class NotTheCircle(PathSlidingScene):
|
||||
def construct(self):
|
||||
self.add_cycloid_end_points()
|
||||
start = self.point_a.get_center()
|
||||
end = self.point_b.get_center()
|
||||
angle = 2*np.pi/3
|
||||
path = Arc(angle, radius = 3)
|
||||
path.gradient_highlight(RED_D, WHITE)
|
||||
radius = Line(ORIGIN, path.points[0])
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
randy_copy = randy.copy()
|
||||
words = TextMobject("Circular paths are good, \\\\ but still not the best")
|
||||
words.shift(UP)
|
||||
|
||||
self.play(
|
||||
ShowCreation(path),
|
||||
ApplyMethod(
|
||||
radius.rotate,
|
||||
angle,
|
||||
path_func = path_along_arc(angle)
|
||||
)
|
||||
)
|
||||
self.play(FadeOut(radius))
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
path.position_endpoints_on, start, end,
|
||||
path_func = path_along_arc(-angle)
|
||||
),
|
||||
run_time = 3
|
||||
)
|
||||
self.adjust_mobject_to_index(randy_copy, 1, path.points)
|
||||
self.play(FadeIn(randy_copy))
|
||||
self.remove(randy_copy)
|
||||
self.slide(randy, path)
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither()
|
||||
|
||||
|
||||
class TransitionAwayFromSlide(PathSlidingScene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
self.add_cycloid_end_points()
|
||||
arrow = Arrow(ORIGIN, 2*RIGHT)
|
||||
arrows = Mobject(*[
|
||||
arrow.copy().shift(vect)
|
||||
for vect in 3*LEFT, ORIGIN, 3*RIGHT
|
||||
])
|
||||
arrows.shift(2*SPACE_WIDTH*RIGHT)
|
||||
self.add(arrows)
|
||||
|
||||
self.add(self.cycloid)
|
||||
self.slide(randy, self.cycloid)
|
||||
everything = Mobject(*self.mobjects)
|
||||
self.play(ApplyMethod(
|
||||
everything.shift, 4*SPACE_WIDTH*LEFT,
|
||||
run_time = 2,
|
||||
rate_func = rush_into
|
||||
))
|
||||
|
||||
|
||||
class MinimalPotentialEnergy(Scene):
|
||||
def construct(self):
|
||||
horiz_radius = 5
|
||||
vert_radius = 3
|
||||
|
||||
vert_axis = NumberLine(numerical_radius = vert_radius)
|
||||
vert_axis.rotate(np.pi/2)
|
||||
vert_axis.shift(horiz_radius*LEFT)
|
||||
horiz_axis = NumberLine(
|
||||
numerical_radius = 5,
|
||||
numbers_with_elongated_ticks = []
|
||||
)
|
||||
axes = Mobject(horiz_axis, vert_axis)
|
||||
graph = FunctionGraph(
|
||||
lambda x : 0.4*(x-2)*(x+2)+3,
|
||||
x_min = -2,
|
||||
x_max = 2,
|
||||
density = 3*DEFAULT_POINT_DENSITY_1D
|
||||
)
|
||||
graph.stretch_to_fit_width(2*horiz_radius)
|
||||
graph.highlight(YELLOW)
|
||||
min_point = Dot(graph.get_bottom())
|
||||
nature_finds = TextMobject("Nature finds this point")
|
||||
nature_finds.scale(0.5)
|
||||
nature_finds.highlight(GREEN)
|
||||
nature_finds.shift(2*RIGHT+3*UP)
|
||||
arrow = Arrow(
|
||||
nature_finds.get_bottom(), min_point,
|
||||
color = GREEN
|
||||
)
|
||||
|
||||
side_words_start = TextMobject("Parameter describing")
|
||||
top_words, last_side_words = [
|
||||
map(TextMobject, pair)
|
||||
for pair in [
|
||||
("Light's travel time", "Potential energy"),
|
||||
("path", "mechanical state")
|
||||
]
|
||||
]
|
||||
for word in top_words + last_side_words + [side_words_start]:
|
||||
word.scale(0.7)
|
||||
side_words_start.next_to(horiz_axis, DOWN)
|
||||
side_words_start.to_edge(RIGHT)
|
||||
for words in top_words:
|
||||
words.next_to(vert_axis, UP)
|
||||
words.to_edge(LEFT)
|
||||
for words in last_side_words:
|
||||
words.next_to(side_words_start, DOWN)
|
||||
for words in top_words[1], last_side_words[1]:
|
||||
words.highlight(RED)
|
||||
|
||||
self.add(
|
||||
axes, top_words[0], side_words_start,
|
||||
last_side_words[0]
|
||||
)
|
||||
self.play(ShowCreation(graph))
|
||||
self.play(
|
||||
ShimmerIn(nature_finds),
|
||||
ShowCreation(arrow),
|
||||
ShowCreation(min_point)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(top_words[0]),
|
||||
FadeOut(last_side_words[0]),
|
||||
GrowFromCenter(top_words[1]),
|
||||
GrowFromCenter(last_side_words[1])
|
||||
)
|
||||
self.dither(3)
|
||||
|
||||
|
||||
|
||||
|
||||
class WhatGovernsSpeed(PathSlidingScene):
|
||||
CONFIG = {
|
||||
"num_pieces" : 6,
|
||||
"dither_and_add" : False,
|
||||
"show_time" : False,
|
||||
}
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
self.add_cycloid_end_points()
|
||||
points = self.cycloid.points
|
||||
ceiling = points[0, 1]
|
||||
n = len(points)
|
||||
broken_points = [
|
||||
points[k*n/self.num_pieces:(k+1)*n/self.num_pieces]
|
||||
for k in range(self.num_pieces)
|
||||
]
|
||||
words = TextMobject("""
|
||||
What determines the speed\\\\
|
||||
at each point?
|
||||
""")
|
||||
words.to_edge(UP)
|
||||
|
||||
self.add(self.cycloid)
|
||||
sliders, vectors = [], []
|
||||
for points in broken_points:
|
||||
path = Mobject().add_points(points)
|
||||
vect = points[-1] - points[-2]
|
||||
magnitude = np.sqrt(ceiling - points[-1, 1])
|
||||
vect = magnitude*vect/np.linalg.norm(vect)
|
||||
slider = self.slide(randy, path, ceiling = ceiling)
|
||||
vector = Vector(slider.get_center(), vect)
|
||||
self.add(slider, vector)
|
||||
sliders.append(slider)
|
||||
vectors.append(vector)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither(3)
|
||||
slider = sliders.pop(1)
|
||||
vector = vectors.pop(1)
|
||||
faders = sliders+vectors+[words]
|
||||
self.play(*map(FadeOut, faders))
|
||||
self.remove(*faders)
|
||||
self.show_geometry(slider, vector)
|
||||
|
||||
def show_geometry(self, slider, vector):
|
||||
point_a = self.point_a.get_center()
|
||||
horiz_line = Line(point_a, point_a + 6*RIGHT)
|
||||
ceil_point = point_a
|
||||
ceil_point[0] = slider.get_center()[0]
|
||||
vert_brace = Brace(
|
||||
Mobject(Point(ceil_point), Point(slider.get_center())),
|
||||
RIGHT,
|
||||
buff = 0.5
|
||||
)
|
||||
vect_brace = Brace(slider)
|
||||
vect_brace.stretch_to_fit_width(vector.get_length())
|
||||
vect_brace.rotate(np.arctan(vector.get_slope()))
|
||||
vect_brace.center().shift(vector.get_center())
|
||||
nudge = 0.2*(DOWN+LEFT)
|
||||
vect_brace.shift(nudge)
|
||||
y_mob = TexMobject("y")
|
||||
y_mob.next_to(vert_brace)
|
||||
sqrt_y = TexMobject("k\\sqrt{y}")
|
||||
sqrt_y.scale(0.5)
|
||||
sqrt_y.shift(vect_brace.get_center())
|
||||
sqrt_y.shift(3*nudge)
|
||||
|
||||
self.play(ShowCreation(horiz_line))
|
||||
self.play(
|
||||
GrowFromCenter(vert_brace),
|
||||
ShimmerIn(y_mob)
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(vect_brace),
|
||||
ShimmerIn(sqrt_y)
|
||||
)
|
||||
self.dither(3)
|
||||
self.solve_energy()
|
||||
|
||||
def solve_energy(self):
|
||||
loss_in_potential = TextMobject("Loss in potential: ")
|
||||
loss_in_potential.shift(2*UP)
|
||||
potential = TexMobject("m g y".split())
|
||||
potential.next_to(loss_in_potential)
|
||||
kinetic = TexMobject([
|
||||
"\\dfrac{1}{2}","m","v","^2","="
|
||||
])
|
||||
kinetic.next_to(potential, LEFT)
|
||||
nudge = 0.1*UP
|
||||
kinetic.shift(nudge)
|
||||
loss_in_potential.shift(nudge)
|
||||
ms = Mobject(kinetic.split()[1], potential.split()[0])
|
||||
two = TexMobject("2")
|
||||
two.shift(ms.split()[1].get_center())
|
||||
half = kinetic.split()[0]
|
||||
sqrt = TexMobject("\\sqrt{\\phantom{2mg}}")
|
||||
sqrt.shift(potential.get_center())
|
||||
nudge = 0.2*LEFT
|
||||
sqrt.shift(nudge)
|
||||
squared = kinetic.split()[3]
|
||||
equals = kinetic.split()[-1]
|
||||
new_eq = equals.copy().next_to(kinetic.split()[2])
|
||||
|
||||
self.play(
|
||||
Transform(
|
||||
Point(loss_in_potential.get_left()),
|
||||
loss_in_potential
|
||||
),
|
||||
*map(GrowFromCenter, potential.split())
|
||||
)
|
||||
self.dither(2)
|
||||
self.play(
|
||||
FadeOut(loss_in_potential),
|
||||
GrowFromCenter(kinetic)
|
||||
)
|
||||
self.dither(2)
|
||||
self.play(ApplyMethod(ms.shift, 5*UP))
|
||||
self.dither()
|
||||
self.play(Transform(
|
||||
half, two,
|
||||
path_func = counterclockwise_path()
|
||||
))
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(
|
||||
squared, sqrt,
|
||||
path_func = clockwise_path()
|
||||
),
|
||||
Transform(equals, new_eq)
|
||||
)
|
||||
self.dither(2)
|
||||
|
||||
|
||||
|
||||
|
||||
class ThetaTInsteadOfXY(Scene):
|
||||
def construct(self):
|
||||
cycloid = Cycloid()
|
||||
index = cycloid.get_num_points()/3
|
||||
point = cycloid.points[index]
|
||||
vect = cycloid.points[index+1]-point
|
||||
vect /= np.linalg.norm(vect)
|
||||
vect *= 3
|
||||
vect_mob = Vector(point, vect)
|
||||
dot = Dot(point)
|
||||
xy = TexMobject("\\big( x(t), y(t) \\big)")
|
||||
xy.next_to(dot, UP+RIGHT, buff = 0.1)
|
||||
vert_line = Line(2*DOWN, 2*UP)
|
||||
vert_line.shift(point)
|
||||
angle = vect_mob.get_angle() + np.pi/2
|
||||
arc = Arc(angle, radius = 1, start_angle = -np.pi/2)
|
||||
arc.shift(point)
|
||||
theta = TexMobject("\\theta(t)")
|
||||
theta.next_to(arc, DOWN, buff = 0.1, aligned_edge = LEFT)
|
||||
theta.shift(0.2*RIGHT)
|
||||
|
||||
self.play(ShowCreation(cycloid))
|
||||
self.play(ShowCreation(dot))
|
||||
self.play(ShimmerIn(xy))
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(xy),
|
||||
ShowCreation(vect_mob)
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
ShowCreation(vert_line),
|
||||
ShimmerIn(theta)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
class DefineCurveWithKnob(PathSlidingScene):
|
||||
def construct(self):
|
||||
self.knob = Circle(color = BLUE_D)
|
||||
self.knob.add_line(UP, DOWN)
|
||||
self.knob.to_corner(UP+RIGHT)
|
||||
self.knob.shift(0.5*DOWN)
|
||||
self.last_angle = np.pi/2
|
||||
arrow = Vector(ORIGIN, RIGHT)
|
||||
arrow.next_to(self.knob, LEFT)
|
||||
words = TextMobject("Turn this knob over time to define the curve")
|
||||
words.next_to(arrow, LEFT)
|
||||
self.path = self.get_path()
|
||||
self.path.shift(1.5*DOWN)
|
||||
self.path.show()
|
||||
self.path.highlight(BLACK)
|
||||
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
|
||||
self.play(ShimmerIn(words))
|
||||
self.play(ShowCreation(arrow))
|
||||
self.play(ShowCreation(self.knob))
|
||||
self.dither()
|
||||
self.add(self.path)
|
||||
|
||||
self.slide(randy, self.path)
|
||||
self.dither()
|
||||
|
||||
|
||||
def get_path(self):
|
||||
return Cycloid(end_theta = 2*np.pi)
|
||||
|
||||
def midslide_action(self, point, angle):
|
||||
d_angle = angle-self.last_angle
|
||||
self.knob.rotate_in_place(d_angle)
|
||||
self.last_angle = angle
|
||||
self.path.highlight(BLUE_D, lambda p : p[0] < point[0])
|
||||
|
||||
|
||||
|
||||
class WonkyDefineCurveWithKnob(DefineCurveWithKnob):
|
||||
def get_path(self):
|
||||
return ParametricFunction(
|
||||
lambda t : t*RIGHT + (-0.2*t-np.sin(2*np.pi*t/6))*UP,
|
||||
start = -7,
|
||||
end = 10
|
||||
)
|
||||
|
||||
|
||||
class SlowDefineCurveWithKnob(DefineCurveWithKnob):
|
||||
def get_path(self):
|
||||
return ParametricFunction(
|
||||
lambda t : t*RIGHT + (np.exp(-(t+2)**2)-0.2*np.exp(t-2)),
|
||||
start = -4,
|
||||
end = 4
|
||||
)
|
||||
|
||||
|
||||
class BumpyDefineCurveWithKnob(DefineCurveWithKnob):
|
||||
def get_path(self):
|
||||
|
||||
result = FunctionGraph(
|
||||
lambda x : 0.05*(x**2)+0.1*np.sin(2*x)
|
||||
)
|
||||
result.rotate(-np.pi/20)
|
||||
result.scale(0.7)
|
||||
result.shift(DOWN)
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
611
old_projects/brachistochrone/cycloid.py
Normal file
611
old_projects/brachistochrone/cycloid.py
Normal file
@ -0,0 +1,611 @@
|
||||
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.curves import *
|
||||
|
||||
class RollAlongVector(Animation):
|
||||
CONFIG = {
|
||||
"rotation_vector" : OUT,
|
||||
}
|
||||
def __init__(self, mobject, vector, **kwargs):
|
||||
radius = mobject.get_width()/2
|
||||
radians = np.linalg.norm(vector)/radius
|
||||
last_alpha = 0
|
||||
digest_config(self, kwargs, locals())
|
||||
Animation.__init__(self, mobject, **kwargs)
|
||||
|
||||
def update_mobject(self, alpha):
|
||||
d_alpha = alpha - self.last_alpha
|
||||
self.last_alpha = alpha
|
||||
self.mobject.rotate_in_place(
|
||||
d_alpha*self.radians,
|
||||
self.rotation_vector
|
||||
)
|
||||
self.mobject.shift(d_alpha*self.vector)
|
||||
|
||||
|
||||
class CycloidScene(Scene):
|
||||
CONFIG = {
|
||||
"point_a" : 6*LEFT+3*UP,
|
||||
"radius" : 2,
|
||||
"end_theta" : 2*np.pi
|
||||
}
|
||||
def construct(self):
|
||||
self.generate_cycloid()
|
||||
self.generate_circle()
|
||||
self.generate_ceiling()
|
||||
|
||||
def grow_parts(self):
|
||||
self.play(*[
|
||||
ShowCreation(mob)
|
||||
for mob in self.circle, self.ceiling
|
||||
])
|
||||
|
||||
def generate_cycloid(self):
|
||||
self.cycloid = Cycloid(
|
||||
point_a = self.point_a,
|
||||
radius = self.radius,
|
||||
end_theta = self.end_theta
|
||||
)
|
||||
|
||||
def generate_circle(self, **kwargs):
|
||||
self.circle = Circle(radius = self.radius, **kwargs)
|
||||
self.circle.shift(self.point_a - self.circle.get_top())
|
||||
radial_line = Line(
|
||||
self.circle.get_center(), self.point_a
|
||||
)
|
||||
self.circle.add(radial_line)
|
||||
|
||||
def generate_ceiling(self):
|
||||
self.ceiling = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT)
|
||||
self.ceiling.shift(self.cycloid.get_top()[1]*UP)
|
||||
|
||||
def draw_cycloid(self, run_time = 3, *anims, **kwargs):
|
||||
kwargs["run_time"] = run_time
|
||||
self.play(
|
||||
RollAlongVector(
|
||||
self.circle,
|
||||
self.cycloid.points[-1]-self.cycloid.points[0],
|
||||
**kwargs
|
||||
),
|
||||
ShowCreation(self.cycloid, **kwargs),
|
||||
*anims
|
||||
)
|
||||
|
||||
def roll_back(self, run_time = 3, *anims, **kwargs):
|
||||
kwargs["run_time"] = run_time
|
||||
self.play(
|
||||
RollAlongVector(
|
||||
self.circle,
|
||||
self.cycloid.points[0]-self.cycloid.points[- 1],
|
||||
rotation_vector = IN,
|
||||
**kwargs
|
||||
),
|
||||
ShowCreation(
|
||||
self.cycloid,
|
||||
rate_func = lambda t : smooth(1-t),
|
||||
**kwargs
|
||||
),
|
||||
*anims
|
||||
)
|
||||
self.generate_cycloid()
|
||||
|
||||
|
||||
class IntroduceCycloid(CycloidScene):
|
||||
def construct(self):
|
||||
CycloidScene.construct(self)
|
||||
|
||||
equation = TexMobject([
|
||||
"\\dfrac{\\sin(\\theta)}{\\sqrt{y}}",
|
||||
"= \\text{constant}"
|
||||
])
|
||||
sin_sqrt, const = equation.split()
|
||||
new_eq = equation.copy()
|
||||
new_eq.to_edge(UP, buff = 1.3)
|
||||
cycloid_word = TextMobject("Cycloid")
|
||||
arrow = Arrow(2*UP, cycloid_word)
|
||||
arrow.reverse_points()
|
||||
q_mark = TextMobject("?")
|
||||
|
||||
self.play(*map(ShimmerIn, equation.split()))
|
||||
self.dither()
|
||||
self.play(
|
||||
ApplyMethod(equation.shift, 2.2*UP),
|
||||
ShowCreation(arrow)
|
||||
)
|
||||
q_mark.next_to(sin_sqrt)
|
||||
self.play(ShimmerIn(cycloid_word))
|
||||
self.dither()
|
||||
self.grow_parts()
|
||||
self.draw_cycloid()
|
||||
self.dither()
|
||||
extra_terms = [const, arrow, cycloid_word]
|
||||
self.play(*[
|
||||
Transform(mob, q_mark)
|
||||
for mob in extra_terms
|
||||
])
|
||||
self.remove(*extra_terms)
|
||||
self.roll_back()
|
||||
q_marks, arrows = self.get_q_marks_and_arrows(sin_sqrt)
|
||||
self.draw_cycloid(3,
|
||||
ShowCreation(q_marks),
|
||||
ShowCreation(arrows)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def get_q_marks_and_arrows(self, mob, n_marks = 10):
|
||||
circle = Circle().replace(mob)
|
||||
q_marks, arrows = result = [Mobject(), Mobject()]
|
||||
for x in range(n_marks):
|
||||
index = (x+0.5)*self.cycloid.get_num_points()/n_marks
|
||||
q_point = self.cycloid.points[index]
|
||||
vect = q_point-mob.get_center()
|
||||
start_point = circle.get_boundary_point(vect)
|
||||
arrow = Arrow(
|
||||
start_point, q_point,
|
||||
color = BLUE_E
|
||||
)
|
||||
|
||||
q_marks.add(TextMobject("?").shift(q_point))
|
||||
arrows.add(arrow)
|
||||
for mob in result:
|
||||
mob.ingest_submobjects()
|
||||
return result
|
||||
|
||||
|
||||
class LeviSolution(CycloidScene):
|
||||
CONFIG = {
|
||||
"cycloid_fraction" : 0.25,
|
||||
}
|
||||
def construct(self):
|
||||
CycloidScene.construct(self)
|
||||
self.add(self.ceiling)
|
||||
self.generate_points()
|
||||
methods = [
|
||||
self.draw_cycloid,
|
||||
self.roll_into_position,
|
||||
self.draw_p_and_c,
|
||||
self.show_pendulum,
|
||||
self.show_diameter,
|
||||
self.show_theta,
|
||||
self.show_similar_triangles,
|
||||
self.show_sin_thetas,
|
||||
self.show_y,
|
||||
self.rearrange,
|
||||
]
|
||||
for method in methods:
|
||||
method()
|
||||
self.dither()
|
||||
|
||||
|
||||
def generate_points(self):
|
||||
index = int(self.cycloid_fraction*self.cycloid.get_num_points())
|
||||
p_point = self.cycloid.points[index]
|
||||
p_dot = Dot(p_point)
|
||||
p_label = TexMobject("P")
|
||||
p_label.next_to(p_dot, DOWN+LEFT)
|
||||
c_point = self.point_a + self.cycloid_fraction*self.radius*2*np.pi*RIGHT
|
||||
c_dot = Dot(c_point)
|
||||
c_label = TexMobject("C")
|
||||
c_label.next_to(c_dot, UP)
|
||||
|
||||
digest_locals(self)
|
||||
|
||||
def roll_into_position(self):
|
||||
self.play(RollAlongVector(
|
||||
self.circle,
|
||||
(1-self.cycloid_fraction)*self.radius*2*np.pi*LEFT,
|
||||
rotation_vector = IN,
|
||||
run_time = 2
|
||||
))
|
||||
|
||||
def draw_p_and_c(self):
|
||||
radial_line = self.circle.submobjects[0] ##Hacky
|
||||
self.play(Transform(radial_line, self.p_dot))
|
||||
self.remove(radial_line)
|
||||
self.add(self.p_dot)
|
||||
self.play(ShimmerIn(self.p_label))
|
||||
self.dither()
|
||||
self.play(Transform(self.ceiling.copy(), self.c_dot))
|
||||
self.play(ShimmerIn(self.c_label))
|
||||
|
||||
def show_pendulum(self, arc_angle = np.pi, arc_color = GREEN):
|
||||
words = TextMobject(": Instantaneous center of rotation")
|
||||
words.next_to(self.c_label)
|
||||
line = Line(self.p_point, self.c_point)
|
||||
line_angle = line.get_angle()+np.pi
|
||||
line_length = line.get_length()
|
||||
line.add(self.p_dot.copy())
|
||||
line.get_center = lambda : self.c_point
|
||||
tangent_line = Line(3*LEFT, 3*RIGHT)
|
||||
tangent_line.rotate(line_angle-np.pi/2)
|
||||
tangent_line.shift(self.p_point)
|
||||
tangent_line.highlight(arc_color)
|
||||
right_angle_symbol = Mobject(
|
||||
Line(UP, UP+RIGHT),
|
||||
Line(UP+RIGHT, RIGHT)
|
||||
)
|
||||
right_angle_symbol.scale(0.3)
|
||||
right_angle_symbol.rotate(tangent_line.get_angle()+np.pi)
|
||||
right_angle_symbol.shift(self.p_point)
|
||||
|
||||
self.play(ShowCreation(line))
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither()
|
||||
pairs = [
|
||||
(line_angle, arc_angle/2),
|
||||
(line_angle+arc_angle/2, -arc_angle),
|
||||
(line_angle-arc_angle/2, arc_angle/2),
|
||||
]
|
||||
arcs = []
|
||||
for start, angle in pairs:
|
||||
arc = Arc(
|
||||
angle = angle,
|
||||
radius = line_length,
|
||||
start_angle = start,
|
||||
color = GREEN
|
||||
)
|
||||
arc.shift(self.c_point)
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
ApplyMethod(
|
||||
line.rotate_in_place,
|
||||
angle,
|
||||
path_func = path_along_arc(angle)
|
||||
),
|
||||
run_time = 2
|
||||
)
|
||||
arcs.append(arc)
|
||||
self.dither()
|
||||
self.play(Transform(arcs[1], tangent_line))
|
||||
self.add(tangent_line)
|
||||
self.play(ShowCreation(right_angle_symbol))
|
||||
self.dither()
|
||||
|
||||
self.tangent_line = tangent_line
|
||||
self.right_angle_symbol = right_angle_symbol
|
||||
self.pc_line = line
|
||||
self.remove(words, *arcs)
|
||||
|
||||
def show_diameter(self):
|
||||
exceptions = [
|
||||
self.circle,
|
||||
self.tangent_line,
|
||||
self.pc_line,
|
||||
self.right_angle_symbol
|
||||
]
|
||||
everything = set(self.mobjects).difference(exceptions)
|
||||
everything_copy = Mobject(*everything).copy()
|
||||
light_everything = everything_copy.copy()
|
||||
dark_everything = everything_copy.copy()
|
||||
dark_everything.fade(0.8)
|
||||
bottom_point = np.array(self.c_point)
|
||||
bottom_point += 2*self.radius*DOWN
|
||||
diameter = Line(bottom_point, self.c_point)
|
||||
brace = Brace(diameter, RIGHT)
|
||||
diameter_word = TextMobject("Diameter")
|
||||
d_mob = TexMobject("D")
|
||||
diameter_word.next_to(brace)
|
||||
d_mob.next_to(diameter)
|
||||
|
||||
self.remove(*everything)
|
||||
self.play(Transform(everything_copy, dark_everything))
|
||||
self.dither()
|
||||
self.play(ShowCreation(diameter))
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.play(ShimmerIn(diameter_word))
|
||||
self.dither()
|
||||
self.play(*[
|
||||
Transform(mob, d_mob)
|
||||
for mob in brace, diameter_word
|
||||
])
|
||||
self.remove(brace, diameter_word)
|
||||
self.add(d_mob)
|
||||
self.play(Transform(everything_copy, light_everything))
|
||||
self.remove(everything_copy)
|
||||
self.add(*everything)
|
||||
|
||||
self.d_mob = d_mob
|
||||
self.bottom_point = bottom_point
|
||||
|
||||
def show_theta(self, radius = 1):
|
||||
arc = Arc(
|
||||
angle = self.tangent_line.get_angle()-np.pi/2,
|
||||
radius = radius,
|
||||
start_angle = np.pi/2
|
||||
)
|
||||
|
||||
theta = TexMobject("\\theta")
|
||||
theta.shift(1.5*arc.get_center())
|
||||
Mobject(arc, theta).shift(self.bottom_point)
|
||||
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
ShimmerIn(theta)
|
||||
)
|
||||
self.arc = arc
|
||||
self.theta = theta
|
||||
|
||||
def show_similar_triangles(self):
|
||||
y_point = np.array(self.p_point)
|
||||
y_point[1] = self.point_a[1]
|
||||
new_arc = Arc(
|
||||
angle = self.tangent_line.get_angle()-np.pi/2,
|
||||
radius = 0.5,
|
||||
start_angle = np.pi
|
||||
)
|
||||
new_arc.shift(self.c_point)
|
||||
new_theta = self.theta.copy()
|
||||
new_theta.next_to(new_arc, LEFT)
|
||||
new_theta.shift(0.1*DOWN)
|
||||
kwargs = {
|
||||
"stroke_width" : 2*DEFAULT_POINT_THICKNESS,
|
||||
}
|
||||
triangle1 = Polygon(
|
||||
self.p_point, self.c_point, self.bottom_point,
|
||||
color = MAROON,
|
||||
**kwargs
|
||||
)
|
||||
triangle2 = Polygon(
|
||||
y_point, self.p_point, self.c_point,
|
||||
color = WHITE,
|
||||
**kwargs
|
||||
)
|
||||
y_line = Line(self.p_point, y_point)
|
||||
|
||||
self.play(
|
||||
Transform(self.arc.copy(), new_arc),
|
||||
Transform(self.theta.copy(), new_theta),
|
||||
run_time = 3
|
||||
)
|
||||
self.dither()
|
||||
self.play(FadeIn(triangle1))
|
||||
self.dither()
|
||||
self.play(Transform(triangle1, triangle2))
|
||||
self.play(ApplyMethod(triangle1.highlight, MAROON))
|
||||
self.dither()
|
||||
self.remove(triangle1)
|
||||
self.add(y_line)
|
||||
|
||||
self.y_line = y_line
|
||||
|
||||
def show_sin_thetas(self):
|
||||
pc = Line(self.p_point, self.c_point)
|
||||
mob = Mobject(self.theta, self.d_mob).copy()
|
||||
mob.ingest_submobjects()
|
||||
triplets = [
|
||||
(pc, "D\\sin(\\theta)", 0.5),
|
||||
(self.y_line, "D\\sin^2(\\theta)", 0.7),
|
||||
]
|
||||
for line, tex, scale in triplets:
|
||||
trig_mob = TexMobject(tex)
|
||||
trig_mob.scale_to_fit_width(
|
||||
scale*line.get_length()
|
||||
)
|
||||
trig_mob.shift(-1.2*trig_mob.get_top())
|
||||
trig_mob.rotate(line.get_angle())
|
||||
trig_mob.shift(line.get_center())
|
||||
if line is self.y_line:
|
||||
trig_mob.shift(0.1*UP)
|
||||
|
||||
self.play(Transform(mob, trig_mob))
|
||||
self.add(trig_mob)
|
||||
self.dither()
|
||||
|
||||
self.remove(mob)
|
||||
self.d_sin_squared_theta = trig_mob
|
||||
|
||||
|
||||
def show_y(self):
|
||||
y_equals = TexMobject(["y", "="])
|
||||
y_equals.shift(2*UP)
|
||||
y_expression = TexMobject([
|
||||
"D ", "\\sin", "^2", "(\\theta)"
|
||||
])
|
||||
y_expression.next_to(y_equals)
|
||||
y_expression.shift(0.05*UP+0.1*RIGHT)
|
||||
temp_expr = self.d_sin_squared_theta.copy()
|
||||
temp_expr.rotate(-np.pi/2)
|
||||
temp_expr.replace(y_expression)
|
||||
y_mob = TexMobject("y")
|
||||
y_mob.next_to(self.y_line, RIGHT)
|
||||
y_mob.shift(0.2*UP)
|
||||
|
||||
self.play(
|
||||
Transform(self.d_sin_squared_theta, temp_expr),
|
||||
ShimmerIn(y_mob),
|
||||
ShowCreation(y_equals)
|
||||
)
|
||||
self.remove(self.d_sin_squared_theta)
|
||||
self.add(y_expression)
|
||||
|
||||
self.y_equals = y_equals
|
||||
self.y_expression = y_expression
|
||||
|
||||
def rearrange(self):
|
||||
sqrt_nudge = 0.2*LEFT
|
||||
y, equals = self.y_equals.split()
|
||||
d, sin, squared, theta = self.y_expression.split()
|
||||
y_sqrt = TexMobject("\\sqrt{\\phantom{y}}")
|
||||
d_sqrt = y_sqrt.copy()
|
||||
y_sqrt.shift(y.get_center()+sqrt_nudge)
|
||||
d_sqrt.shift(d.get_center()+sqrt_nudge)
|
||||
|
||||
self.play(
|
||||
ShimmerIn(y_sqrt),
|
||||
ShimmerIn(d_sqrt),
|
||||
ApplyMethod(squared.shift, 4*UP),
|
||||
ApplyMethod(theta.shift, 1.5* squared.get_width()*LEFT)
|
||||
)
|
||||
self.dither()
|
||||
y_sqrt.add(y)
|
||||
d_sqrt.add(d)
|
||||
sin.add(theta)
|
||||
|
||||
sin_over = TexMobject("\\dfrac{\\phantom{\\sin(\\theta)}}{\\quad}")
|
||||
sin_over.next_to(sin, DOWN, 0.15)
|
||||
new_eq = equals.copy()
|
||||
new_eq.next_to(sin_over, LEFT)
|
||||
one_over = TexMobject("\\dfrac{1}{\\quad}")
|
||||
one_over.next_to(new_eq, LEFT)
|
||||
one_over.shift(
|
||||
(sin_over.get_bottom()[1]-one_over.get_bottom()[1])*UP
|
||||
)
|
||||
|
||||
self.play(
|
||||
Transform(equals, new_eq),
|
||||
ShimmerIn(sin_over),
|
||||
ShimmerIn(one_over),
|
||||
ApplyMethod(
|
||||
d_sqrt.next_to, one_over, DOWN,
|
||||
path_func = path_along_arc(-np.pi)
|
||||
),
|
||||
ApplyMethod(
|
||||
y_sqrt.next_to, sin_over, DOWN,
|
||||
path_func = path_along_arc(-np.pi)
|
||||
),
|
||||
run_time = 2
|
||||
)
|
||||
self.dither()
|
||||
|
||||
brace = Brace(d_sqrt, DOWN)
|
||||
constant = TextMobject("Constant")
|
||||
constant.next_to(brace, DOWN)
|
||||
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
ShimmerIn(constant)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class EquationsForCycloid(CycloidScene):
|
||||
def construct(self):
|
||||
CycloidScene.construct(self)
|
||||
equations = TexMobject([
|
||||
"x(t) = Rt - R\\sin(t)",
|
||||
"y(t) = -R + R\\cos(t)"
|
||||
])
|
||||
top, bottom = equations.split()
|
||||
bottom.next_to(top, DOWN)
|
||||
equations.center()
|
||||
equations.to_edge(UP, buff = 1.3)
|
||||
|
||||
self.play(ShimmerIn(equations))
|
||||
self.grow_parts()
|
||||
self.draw_cycloid(rate_func = None, run_time = 5)
|
||||
self.dither()
|
||||
|
||||
class SlidingObject(CycloidScene, PathSlidingScene):
|
||||
CONFIG = {
|
||||
"show_time" : False,
|
||||
"dither_and_add" : False
|
||||
}
|
||||
|
||||
args_list = [(True,), (False,)]
|
||||
|
||||
@staticmethod
|
||||
def args_to_string(with_words):
|
||||
return "WithWords" if with_words else "WithoutWords"
|
||||
|
||||
@staticmethod
|
||||
def string_to_args(string):
|
||||
return string == "WithWords"
|
||||
|
||||
def construct(self, with_words):
|
||||
CycloidScene.construct(self)
|
||||
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
central_randy = randy.copy()
|
||||
start_randy = self.adjust_mobject_to_index(
|
||||
randy.copy(), 1, self.cycloid.points
|
||||
)
|
||||
|
||||
if with_words:
|
||||
words1 = TextMobject("Trajectory due to gravity")
|
||||
arrow = TexMobject("\\leftrightarrow")
|
||||
words2 = TextMobject("Trajectory due \\emph{constantly} rotating wheel")
|
||||
words1.next_to(arrow, LEFT)
|
||||
words2.next_to(arrow, RIGHT)
|
||||
words = Mobject(words1, arrow, words2)
|
||||
words.scale_to_fit_width(2*SPACE_WIDTH-1)
|
||||
words.to_edge(UP, buff = 0.2)
|
||||
words.to_edge(LEFT)
|
||||
|
||||
self.play(ShowCreation(self.cycloid.copy()))
|
||||
self.slide(randy, self.cycloid)
|
||||
self.add(self.slider)
|
||||
self.dither()
|
||||
self.grow_parts()
|
||||
self.draw_cycloid()
|
||||
self.dither()
|
||||
self.play(Transform(self.slider, start_randy))
|
||||
self.dither()
|
||||
self.roll_back()
|
||||
self.dither()
|
||||
if with_words:
|
||||
self.play(*map(ShimmerIn, [words1, arrow, words2]))
|
||||
self.dither()
|
||||
self.remove(self.circle)
|
||||
start_time = len(self.frames)*self.frame_duration
|
||||
self.remove(self.slider)
|
||||
self.slide(central_randy, self.cycloid)
|
||||
end_time = len(self.frames)*self.frame_duration
|
||||
self.play_over_time_range(
|
||||
start_time,
|
||||
end_time,
|
||||
RollAlongVector(
|
||||
self.circle,
|
||||
self.cycloid.points[-1]-self.cycloid.points[0],
|
||||
run_time = end_time-start_time,
|
||||
rate_func = None
|
||||
)
|
||||
)
|
||||
self.add(self.circle, self.slider)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class RotateWheel(CycloidScene):
|
||||
def construct(self):
|
||||
CycloidScene.construct(self)
|
||||
self.circle.center()
|
||||
|
||||
self.play(Rotating(
|
||||
self.circle,
|
||||
axis = OUT,
|
||||
run_time = 5,
|
||||
rate_func = smooth
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
429
old_projects/brachistochrone/drawing_images.py
Normal file
429
old_projects/brachistochrone/drawing_images.py
Normal file
@ -0,0 +1,429 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import operator as op
|
||||
import sys
|
||||
import inspect
|
||||
from PIL import Image
|
||||
import cv2
|
||||
import random
|
||||
from scipy.spatial.distance import cdist
|
||||
from scipy import ndimage
|
||||
|
||||
from helpers import *
|
||||
|
||||
from mobject.tex_mobject import TexMobject
|
||||
from mobject import Mobject
|
||||
from mobject.image_mobject import \
|
||||
ImageMobject, MobjectFromPixelArray
|
||||
from mobject.tex_mobject import TextMobject, TexMobject
|
||||
|
||||
from animation.transform import \
|
||||
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||
FadeIn, FadeOut, GrowFromCenter, ShimmerIn, ApplyMethod
|
||||
from animation.simple_animations import \
|
||||
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder
|
||||
from animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import \
|
||||
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point
|
||||
from topics.characters import Randolph, Mathematician, ThoughtBubble
|
||||
from topics.functions import ParametricFunction
|
||||
from topics.number_line import NumberPlane
|
||||
from mobject.region import Region, region_from_polygon_vertices
|
||||
from scene import Scene
|
||||
|
||||
|
||||
DEFAULT_GAUSS_BLUR_CONFIG = {
|
||||
"ksize" : (5, 5),
|
||||
"sigmaX" : 6,
|
||||
"sigmaY" : 6,
|
||||
}
|
||||
|
||||
DEFAULT_CANNY_CONFIG = {
|
||||
"threshold1" : 50,
|
||||
"threshold2" : 100,
|
||||
}
|
||||
|
||||
DEFAULT_BLUR_RADIUS = 0.5
|
||||
DEFAULT_CONNECTED_COMPONENT_THRESHOLD = 25
|
||||
|
||||
|
||||
def reverse_colors(nparray):
|
||||
return nparray[:,:,[2, 1, 0]]
|
||||
|
||||
def show(nparray):
|
||||
Image.fromarray(reverse_colors(nparray)).show()
|
||||
|
||||
|
||||
def thicken(nparray):
|
||||
height, width = nparray.shape
|
||||
nparray = nparray.reshape((height, width, 1))
|
||||
return np.repeat(nparray, 3, 2)
|
||||
|
||||
def sort_by_color(mob):
|
||||
indices = np.argsort(np.apply_along_axis(
|
||||
lambda p : -np.linalg.norm(p),
|
||||
1,
|
||||
mob.rgbs
|
||||
))
|
||||
mob.rgbs = mob.rgbs[indices]
|
||||
mob.points = mob.points[indices]
|
||||
|
||||
|
||||
|
||||
def get_image_array(name):
|
||||
image_files = os.listdir(IMAGE_DIR)
|
||||
possibilities = filter(lambda s : s.startswith(name), image_files)
|
||||
for possibility in possibilities:
|
||||
try:
|
||||
path = os.path.join(IMAGE_DIR, possibility)
|
||||
image = Image.open(path)
|
||||
image = image.convert('RGB')
|
||||
return np.array(image)
|
||||
except:
|
||||
pass
|
||||
raise Exception("Image for %s not found"%name)
|
||||
|
||||
def get_edges(image_array):
|
||||
blurred = cv2.GaussianBlur(
|
||||
image_array,
|
||||
**DEFAULT_GAUSS_BLUR_CONFIG
|
||||
)
|
||||
edges = cv2.Canny(
|
||||
blurred,
|
||||
**DEFAULT_CANNY_CONFIG
|
||||
)
|
||||
return edges
|
||||
|
||||
def nearest_neighbor_align(mobject1, mobject2):
|
||||
distance_matrix = cdist(mobject1.points, mobject2.points)
|
||||
closest_point_indices = np.apply_along_axis(
|
||||
np.argmin, 0, distance_matrix
|
||||
)
|
||||
new_mob1 = Mobject()
|
||||
new_mob2 = Mobject()
|
||||
for n in range(mobject1.get_num_points()):
|
||||
indices = (closest_point_indices == n)
|
||||
new_mob1.add_points(
|
||||
[mobject1.points[n]]*sum(indices)
|
||||
)
|
||||
new_mob2.add_points(
|
||||
mobject2.points[indices],
|
||||
rgbs = mobject2.rgbs[indices]
|
||||
)
|
||||
return new_mob1, new_mob2
|
||||
|
||||
def get_connected_components(image_array,
|
||||
blur_radius = DEFAULT_BLUR_RADIUS,
|
||||
threshold = DEFAULT_CONNECTED_COMPONENT_THRESHOLD):
|
||||
blurred_image = ndimage.gaussian_filter(image_array, blur_radius)
|
||||
labels, component_count = ndimage.label(blurred_image > threshold)
|
||||
return [
|
||||
image_array * (labels == count)
|
||||
for count in range(1, component_count+1)
|
||||
]
|
||||
|
||||
def color_region(bw_region, colored_image):
|
||||
return thicken(bw_region > 0) * colored_image
|
||||
|
||||
|
||||
class TracePicture(Scene):
|
||||
args_list = [
|
||||
("Newton",),
|
||||
("Mark_Levi",),
|
||||
("Steven_Strogatz",),
|
||||
("Pierre_de_Fermat",),
|
||||
("Galileo_Galilei",),
|
||||
("Jacob_Bernoulli",),
|
||||
("Johann_Bernoulli2",),
|
||||
("Old_Newton",)
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def args_to_string(name):
|
||||
return name
|
||||
|
||||
@staticmethod
|
||||
def string_to_args(name):
|
||||
return name
|
||||
|
||||
def construct(self, name):
|
||||
run_time = 20
|
||||
scale_factor = 0.8
|
||||
image_array = get_image_array(name)
|
||||
edge_mobject = self.get_edge_mobject(image_array)
|
||||
full_picture = MobjectFromPixelArray(image_array)
|
||||
for mob in edge_mobject, full_picture:
|
||||
# mob.stroke_width = 4
|
||||
mob.scale(scale_factor)
|
||||
mob.show()
|
||||
|
||||
self.play(
|
||||
DelayByOrder(FadeIn(
|
||||
full_picture,
|
||||
run_time = run_time,
|
||||
rate_func = squish_rate_func(smooth, 0.7, 1)
|
||||
)),
|
||||
ShowCreation(
|
||||
edge_mobject,
|
||||
run_time = run_time,
|
||||
rate_func = None
|
||||
)
|
||||
)
|
||||
self.remove(edge_mobject)
|
||||
self.dither()
|
||||
|
||||
|
||||
def get_edge_mobject(self, image_array):
|
||||
edged_image = get_edges(image_array)
|
||||
individual_edges = get_connected_components(edged_image)
|
||||
colored_edges = [
|
||||
color_region(edge, image_array)
|
||||
for edge in individual_edges
|
||||
]
|
||||
colored_edge_mobject_list = [
|
||||
MobjectFromPixelArray(colored_edge)
|
||||
for colored_edge in colored_edges
|
||||
]
|
||||
random.shuffle(colored_edge_mobject_list)
|
||||
edge_mobject = Mobject(*colored_edge_mobject_list)
|
||||
edge_mobject.ingest_submobjects()
|
||||
return edge_mobject
|
||||
|
||||
|
||||
|
||||
class JohannThinksHeIsBetter(Scene):
|
||||
def construct(self):
|
||||
names = [
|
||||
"Johann_Bernoulli2",
|
||||
"Jacob_Bernoulli",
|
||||
"Gottfried_Wilhelm_von_Leibniz",
|
||||
"Newton"
|
||||
]
|
||||
guys = [
|
||||
ImageMobject(name, invert = False)
|
||||
for name in names
|
||||
]
|
||||
johann = guys[0]
|
||||
johann.scale(0.8)
|
||||
pensive_johann = johann.copy()
|
||||
pensive_johann.scale(0.25)
|
||||
pensive_johann.to_corner(DOWN+LEFT)
|
||||
comparitive_johann = johann.copy()
|
||||
template = Square(side_length = 2)
|
||||
comparitive_johann.replace(template)
|
||||
comparitive_johann.shift(UP+LEFT)
|
||||
greater_than = TexMobject(">")
|
||||
greater_than.next_to(comparitive_johann)
|
||||
for guy, name in zip(guys, names)[1:]:
|
||||
guy.replace(template)
|
||||
guy.next_to(greater_than)
|
||||
name_mob = TextMobject(name.replace("_", " "))
|
||||
name_mob.scale(0.5)
|
||||
name_mob.next_to(guy, DOWN)
|
||||
guy.name_mob = name_mob
|
||||
guy.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
||||
bubble = ThoughtBubble(initial_width = 12)
|
||||
bubble.stretch_to_fit_height(6)
|
||||
bubble.ingest_submobjects()
|
||||
bubble.pin_to(pensive_johann)
|
||||
bubble.shift(DOWN)
|
||||
point = Point(johann.get_corner(UP+RIGHT))
|
||||
upper_point = Point(comparitive_johann.get_corner(UP+RIGHT))
|
||||
lightbulb = ImageMobject("Lightbulb", invert = False)
|
||||
lightbulb.scale(0.1)
|
||||
lightbulb.sort_points(np.linalg.norm)
|
||||
lightbulb.next_to(upper_point, RIGHT)
|
||||
|
||||
self.add(johann)
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(johann, pensive_johann),
|
||||
Transform(point, bubble),
|
||||
run_time = 2
|
||||
)
|
||||
self.remove(point)
|
||||
self.add(bubble)
|
||||
weakling = guys[1]
|
||||
self.play(
|
||||
FadeIn(comparitive_johann),
|
||||
ShowCreation(greater_than),
|
||||
FadeIn(weakling)
|
||||
)
|
||||
self.dither(2)
|
||||
for guy in guys[2:]:
|
||||
self.play(DelayByOrder(Transform(
|
||||
weakling, upper_point
|
||||
)))
|
||||
self.play(
|
||||
FadeIn(guy),
|
||||
ShimmerIn(guy.name_mob)
|
||||
)
|
||||
self.dither(3)
|
||||
self.remove(guy.name_mob)
|
||||
weakling = guy
|
||||
self.play(FadeOut(weakling), FadeOut(greater_than))
|
||||
self.play(ShowCreation(lightbulb))
|
||||
self.dither()
|
||||
self.play(FadeOut(comparitive_johann), FadeOut(lightbulb))
|
||||
self.play(ApplyMethod(
|
||||
Mobject(johann, bubble).scale, 10,
|
||||
run_time = 3
|
||||
))
|
||||
|
||||
|
||||
class NewtonVsJohann(Scene):
|
||||
def construct(self):
|
||||
newton, johann = [
|
||||
ImageMobject(name, invert = False).scale(0.5)
|
||||
for name in "Newton", "Johann_Bernoulli2"
|
||||
]
|
||||
greater_than = TexMobject(">")
|
||||
newton.next_to(greater_than, RIGHT)
|
||||
johann.next_to(greater_than, LEFT)
|
||||
self.add(johann, greater_than, newton)
|
||||
for i in range(2):
|
||||
kwargs = {
|
||||
"path_func" : counterclockwise_path(),
|
||||
"run_time" : 2
|
||||
}
|
||||
self.play(
|
||||
ApplyMethod(newton.replace, johann, **kwargs),
|
||||
ApplyMethod(johann.replace, newton, **kwargs),
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
class JohannThinksOfFermat(Scene):
|
||||
def construct(self):
|
||||
johann, fermat = [
|
||||
ImageMobject(name, invert = False)
|
||||
for name in "Johann_Bernoulli2", "Pierre_de_Fermat"
|
||||
]
|
||||
johann.scale(0.2)
|
||||
johann.to_corner(DOWN+LEFT)
|
||||
bubble = ThoughtBubble(initial_width = 12)
|
||||
bubble.stretch_to_fit_height(6)
|
||||
bubble.pin_to(johann)
|
||||
bubble.shift(DOWN)
|
||||
bubble.add_content(fermat)
|
||||
fermat.scale_in_place(0.4)
|
||||
|
||||
|
||||
self.add(johann, bubble)
|
||||
self.dither()
|
||||
self.play(FadeIn(fermat))
|
||||
self.dither()
|
||||
|
||||
|
||||
class MathematiciansOfEurope(Scene):
|
||||
def construct(self):
|
||||
europe = ImageMobject("Europe", use_cache = False)
|
||||
self.add(europe)
|
||||
self.freeze_background()
|
||||
|
||||
mathematicians = [
|
||||
("Newton", [-1.75, -0.75, 0]),
|
||||
("Jacob_Bernoulli",[-0.75, -1.75, 0]),
|
||||
("Ehrenfried_von_Tschirnhaus",[0.5, -0.5, 0]),
|
||||
("Gottfried_Wilhelm_von_Leibniz",[0.2, -1.75, 0]),
|
||||
("Guillaume_de_L'Hopital", [-1.75, -1.25, 0]),
|
||||
]
|
||||
|
||||
for name, point in mathematicians:
|
||||
man = ImageMobject(name, invert = False)
|
||||
if name == "Newton":
|
||||
name = "Isaac_Newton"
|
||||
name_mob = TextMobject(name.replace("_", " "))
|
||||
name_mob.to_corner(UP+LEFT, buff=0.75)
|
||||
self.add(name_mob)
|
||||
man.scale_to_fit_height(4)
|
||||
mobject = Point(man.get_corner(UP+LEFT))
|
||||
self.play(Transform(mobject, man))
|
||||
man.scale(0.2)
|
||||
man.shift(point)
|
||||
self.play(Transform(mobject, man))
|
||||
self.remove(name_mob)
|
||||
|
||||
class OldNewtonIsDispleased(Scene):
|
||||
def construct(self):
|
||||
old_newton = ImageMobject("Old_Newton", invert = False)
|
||||
old_newton.scale(0.8)
|
||||
self.add(old_newton)
|
||||
self.freeze_background()
|
||||
|
||||
words = TextMobject("Note the displeasure")
|
||||
words.to_corner(UP+RIGHT)
|
||||
face_point = 1.8*UP+0.5*LEFT
|
||||
arrow = Arrow(words.get_bottom(), face_point)
|
||||
|
||||
|
||||
self.play(ShimmerIn(words))
|
||||
self.play(ShowCreation(arrow))
|
||||
self.dither()
|
||||
|
||||
|
||||
class NewtonConsideredEveryoneBeneathHim(Scene):
|
||||
def construct(self):
|
||||
mathematicians = [
|
||||
ImageMobject(name, invert = False)
|
||||
for name in [
|
||||
"Old_Newton",
|
||||
"Johann_Bernoulli2",
|
||||
"Jacob_Bernoulli",
|
||||
"Ehrenfried_von_Tschirnhaus",
|
||||
"Gottfried_Wilhelm_von_Leibniz",
|
||||
"Guillaume_de_L'Hopital",
|
||||
]
|
||||
]
|
||||
newton = mathematicians.pop(0)
|
||||
newton.scale(0.8)
|
||||
new_newton = newton.copy()
|
||||
new_newton.scale_to_fit_height(3)
|
||||
new_newton.to_edge(UP)
|
||||
for man in mathematicians:
|
||||
man.scale_to_fit_width(1.7)
|
||||
johann = mathematicians.pop(0)
|
||||
johann.next_to(new_newton, DOWN)
|
||||
last_left, last_right = johann, johann
|
||||
for man, count in zip(mathematicians, it.count()):
|
||||
if count%2 == 0:
|
||||
man.next_to(last_left, LEFT)
|
||||
last_left = man
|
||||
else:
|
||||
man.next_to(last_right, RIGHT)
|
||||
last_right = man
|
||||
|
||||
self.play(
|
||||
Transform(newton, new_newton),
|
||||
GrowFromCenter(johann)
|
||||
)
|
||||
self.dither()
|
||||
self.play(FadeIn(Mobject(*mathematicians)))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
344
old_projects/brachistochrone/graveyard.py
Normal file
344
old_projects/brachistochrone/graveyard.py
Normal file
@ -0,0 +1,344 @@
|
||||
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 animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import *
|
||||
from topics.characters import Randolph, Mathematician
|
||||
from topics.functions import *
|
||||
from topics.number_line import *
|
||||
from mobject.region import Region, region_from_polygon_vertices
|
||||
from scene import Scene
|
||||
from scene.zoomed_scene import ZoomedScene
|
||||
|
||||
from 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_space_shape" : (5, 5),
|
||||
"square_color" : GREEN_B,
|
||||
}
|
||||
def construct(self):
|
||||
self.cycloid = Cycloid(end_theta = np.pi)
|
||||
self.cycloid.highlight(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.dither()
|
||||
self.play(ShowCreation(
|
||||
continuous,
|
||||
rate_func = lambda t : smooth(1-t)
|
||||
))
|
||||
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)
|
||||
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], SPACE_WIDTH*RIGHT+(self.layer_tops[-1]-1)*UP
|
||||
)
|
||||
|
||||
def show_layer_variables(self):
|
||||
layer_top_pairs = 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+SPACE_WIDTH*LEFT,
|
||||
midpoint+SPACE_WIDTH*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 = None
|
||||
)
|
||||
self.play(
|
||||
ShimmerIn(v_eq[0]),
|
||||
photon_run,
|
||||
run_time = time
|
||||
)
|
||||
self.dither()
|
||||
for start_y, brace in zip(start_ys, braces):
|
||||
self.add(start_y)
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.dither()
|
||||
quads = 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(*map(ShowCreation, figure_marks))
|
||||
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)
|
||||
)
|
||||
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.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)
|
943
old_projects/brachistochrone/light.py
Normal file
943
old_projects/brachistochrone/light.py
Normal file
@ -0,0 +1,943 @@
|
||||
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 animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import *
|
||||
from topics.characters import Randolph, Mathematician
|
||||
from topics.functions import *
|
||||
from topics.number_line import *
|
||||
from mobject.region import Region, region_from_polygon_vertices
|
||||
from scene import Scene
|
||||
from scene.zoomed_scene import ZoomedScene
|
||||
|
||||
from brachistochrone.curves import \
|
||||
Cycloid, PathSlidingScene, RANDY_SCALE_VAL, TryManyPaths
|
||||
|
||||
|
||||
class Lens(Arc):
|
||||
CONFIG = {
|
||||
"radius" : 2,
|
||||
"angle" : np.pi/2,
|
||||
"color" : BLUE_B,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
Arc.__init__(self, self.angle, **kwargs)
|
||||
|
||||
def generate_points(self):
|
||||
Arc.generate_points(self)
|
||||
self.rotate(-np.pi/4)
|
||||
self.shift(-self.get_left())
|
||||
self.add_points(self.copy().rotate(np.pi).points)
|
||||
|
||||
|
||||
|
||||
class PhotonScene(Scene):
|
||||
def wavify(self, mobject):
|
||||
result = mobject.copy()
|
||||
result.ingest_submobjects()
|
||||
tangent_vectors = result.points[1:]-result.points[:-1]
|
||||
lengths = np.apply_along_axis(
|
||||
np.linalg.norm, 1, tangent_vectors
|
||||
)
|
||||
thick_lengths = lengths.repeat(3).reshape((len(lengths), 3))
|
||||
unit_tangent_vectors = tangent_vectors/thick_lengths
|
||||
rot_matrix = np.transpose(rotation_matrix(np.pi/2, OUT))
|
||||
normal_vectors = np.dot(unit_tangent_vectors, rot_matrix)
|
||||
# total_length = np.sum(lengths)
|
||||
times = np.cumsum(lengths)
|
||||
nudge_sizes = 0.1*np.sin(2*np.pi*times)
|
||||
thick_nudge_sizes = nudge_sizes.repeat(3).reshape((len(nudge_sizes), 3))
|
||||
nudges = thick_nudge_sizes*normal_vectors
|
||||
result.points[1:] += nudges
|
||||
return result
|
||||
|
||||
|
||||
def photon_run_along_path(self, path, color = YELLOW, **kwargs):
|
||||
if "rate_func" not in kwargs:
|
||||
kwargs["rate_func"] = None
|
||||
photon = self.wavify(path)
|
||||
photon.highlight(color)
|
||||
return ShowPassingFlash(photon, **kwargs)
|
||||
|
||||
|
||||
class SimplePhoton(PhotonScene):
|
||||
def construct(self):
|
||||
text = TextMobject("Light")
|
||||
text.to_edge(UP)
|
||||
self.play(ShimmerIn(text))
|
||||
self.play(self.photon_run_along_path(
|
||||
Cycloid(), rate_func = None
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
class MultipathPhotonScene(PhotonScene):
|
||||
CONFIG = {
|
||||
"num_paths" : 5
|
||||
}
|
||||
def run_along_paths(self, **kwargs):
|
||||
paths = self.get_paths()
|
||||
colors = Color(YELLOW).range_to(WHITE, len(paths))
|
||||
for path, color in zip(paths, colors):
|
||||
path.highlight(color)
|
||||
photon_runs = [
|
||||
self.photon_run_along_path(path)
|
||||
for path in paths
|
||||
]
|
||||
for photon_run, path in zip(photon_runs, paths):
|
||||
self.play(
|
||||
photon_run,
|
||||
ShowCreation(
|
||||
path,
|
||||
rate_func = lambda t : 0.9*smooth(t)
|
||||
),
|
||||
**kwargs
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def generate_paths(self):
|
||||
raise Exception("Not Implemented")
|
||||
|
||||
|
||||
class PhotonThroughLens(MultipathPhotonScene):
|
||||
def construct(self):
|
||||
self.lens = Lens()
|
||||
self.add(self.lens)
|
||||
self.run_along_paths()
|
||||
|
||||
|
||||
def get_paths(self):
|
||||
interval_values = np.arange(self.num_paths).astype('float')
|
||||
interval_values /= (self.num_paths-1.)
|
||||
first_contact = [
|
||||
self.lens.point_from_proportion(0.4*v+0.55)
|
||||
for v in reversed(interval_values)
|
||||
]
|
||||
second_contact = [
|
||||
self.lens.point_from_proportion(0.3*v + 0.1)
|
||||
for v in interval_values
|
||||
]
|
||||
focal_point = 2*RIGHT
|
||||
return [
|
||||
Mobject(
|
||||
Line(SPACE_WIDTH*LEFT + fc[1]*UP, fc),
|
||||
Line(fc, sc),
|
||||
Line(sc, focal_point),
|
||||
Line(focal_point, 6*focal_point-5*sc)
|
||||
).ingest_submobjects()
|
||||
for fc, sc in zip(first_contact, second_contact)
|
||||
]
|
||||
|
||||
class TransitionToOptics(PhotonThroughLens):
|
||||
def construct(self):
|
||||
optics = TextMobject("Optics")
|
||||
optics.to_edge(UP)
|
||||
self.add(optics)
|
||||
self.has_started = False
|
||||
PhotonThroughLens.construct(self)
|
||||
|
||||
def play(self, *args, **kwargs):
|
||||
if not self.has_started:
|
||||
self.has_started = True
|
||||
everything = Mobject(*self.mobjects)
|
||||
vect = 2*SPACE_WIDTH*RIGHT
|
||||
everything.shift(vect)
|
||||
self.play(ApplyMethod(
|
||||
everything.shift, -vect,
|
||||
rate_func = rush_from
|
||||
))
|
||||
Scene.play(self, *args, **kwargs)
|
||||
|
||||
|
||||
class PhotonOffMirror(MultipathPhotonScene):
|
||||
def construct(self):
|
||||
self.mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP]))
|
||||
self.mirror.highlight(GREY)
|
||||
self.add(self.mirror)
|
||||
self.run_along_paths()
|
||||
|
||||
def get_paths(self):
|
||||
interval_values = np.arange(self.num_paths).astype('float')
|
||||
interval_values /= (self.num_paths-1)
|
||||
anchor_points = [
|
||||
self.mirror.point_from_proportion(0.6*v+0.3)
|
||||
for v in interval_values
|
||||
]
|
||||
start_point = 5*LEFT+3*UP
|
||||
end_points = []
|
||||
for point in anchor_points:
|
||||
vect = start_point-point
|
||||
vect[1] *= -1
|
||||
end_points.append(point+2*vect)
|
||||
return [
|
||||
Mobject(
|
||||
Line(start_point, anchor_point),
|
||||
Line(anchor_point, end_point)
|
||||
).ingest_submobjects()
|
||||
for anchor_point, end_point in zip(anchor_points, end_points)
|
||||
]
|
||||
|
||||
class PhotonsInWater(MultipathPhotonScene):
|
||||
def construct(self):
|
||||
water = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
self.add(water)
|
||||
self.run_along_paths()
|
||||
|
||||
def get_paths(self):
|
||||
x, y = -3, 3
|
||||
start_point = x*RIGHT + y*UP
|
||||
angles = np.arange(np.pi/18, np.pi/3, np.pi/18)
|
||||
midpoints = y*np.arctan(angles)
|
||||
end_points = midpoints + SPACE_HEIGHT*np.arctan(2*angles)
|
||||
return [
|
||||
Mobject(
|
||||
Line(start_point, [midpoint, 0, 0]),
|
||||
Line([midpoint, 0, 0], [end_point, -SPACE_HEIGHT, 0])
|
||||
).ingest_submobjects()
|
||||
for midpoint, end_point in zip(midpoints, end_points)
|
||||
]
|
||||
|
||||
|
||||
class ShowMultiplePathsScene(PhotonScene):
|
||||
def construct(self):
|
||||
text = TextMobject("Which path minimizes travel time?")
|
||||
text.to_edge(UP)
|
||||
self.generate_start_and_end_points()
|
||||
point_a = Dot(self.start_point)
|
||||
point_b = Dot(self.end_point)
|
||||
A = TextMobject("A").next_to(point_a, UP)
|
||||
B = TextMobject("B").next_to(point_b, DOWN)
|
||||
paths = self.get_paths()
|
||||
|
||||
for point, letter in [(point_a, A), (point_b, B)]:
|
||||
self.play(
|
||||
ShowCreation(point),
|
||||
ShimmerIn(letter)
|
||||
)
|
||||
self.play(ShimmerIn(text))
|
||||
curr_path = paths[0].copy()
|
||||
curr_path_copy = curr_path.copy().ingest_submobjects()
|
||||
self.play(
|
||||
self.photon_run_along_path(curr_path),
|
||||
ShowCreation(curr_path_copy, rate_func = rush_into)
|
||||
)
|
||||
self.remove(curr_path_copy)
|
||||
for path in paths[1:] + [paths[0]]:
|
||||
self.play(Transform(curr_path, path, run_time = 4))
|
||||
self.dither()
|
||||
self.path = curr_path.ingest_submobjects()
|
||||
|
||||
def generate_start_and_end_points(self):
|
||||
raise Exception("Not Implemented")
|
||||
|
||||
def get_paths(self):
|
||||
raise Exception("Not implemented")
|
||||
|
||||
|
||||
class ShowMultiplePathsThroughLens(ShowMultiplePathsScene):
|
||||
def construct(self):
|
||||
self.lens = Lens()
|
||||
self.add(self.lens)
|
||||
ShowMultiplePathsScene.construct(self)
|
||||
|
||||
def generate_start_and_end_points(self):
|
||||
self.start_point = 3*LEFT + UP
|
||||
self.end_point = 2*RIGHT
|
||||
|
||||
def get_paths(self):
|
||||
alphas = [0.25, 0.4, 0.58, 0.75]
|
||||
lower_right, upper_right, upper_left, lower_left = map(
|
||||
self.lens.point_from_proportion, alphas
|
||||
)
|
||||
return [
|
||||
Mobject(
|
||||
Line(self.start_point, a),
|
||||
Line(a, b),
|
||||
Line(b, self.end_point)
|
||||
).highlight(color)
|
||||
for (a, b), color in zip(
|
||||
[
|
||||
(upper_left, upper_right),
|
||||
(upper_left, lower_right),
|
||||
(lower_left, lower_right),
|
||||
(lower_left, upper_right),
|
||||
],
|
||||
Color(YELLOW).range_to(WHITE, 4)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class ShowMultiplePathsOffMirror(ShowMultiplePathsScene):
|
||||
def construct(self):
|
||||
mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP]))
|
||||
mirror.highlight(GREY)
|
||||
self.add(mirror)
|
||||
ShowMultiplePathsScene.construct(self)
|
||||
|
||||
def generate_start_and_end_points(self):
|
||||
self.start_point = 4*LEFT + 2*UP
|
||||
self.end_point = 4*LEFT + 2*DOWN
|
||||
|
||||
def get_paths(self):
|
||||
return [
|
||||
Mobject(
|
||||
Line(self.start_point, midpoint),
|
||||
Line(midpoint, self.end_point)
|
||||
).highlight(color)
|
||||
for midpoint, color in zip(
|
||||
[2*UP, 2*DOWN],
|
||||
Color(YELLOW).range_to(WHITE, 2)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class ShowMultiplePathsInWater(ShowMultiplePathsScene):
|
||||
def construct(self):
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
self.generate_start_and_end_points()
|
||||
straight = Line(self.start_point, self.end_point)
|
||||
slow = TextMobject("Slow")
|
||||
slow.rotate(np.arctan(straight.get_slope()))
|
||||
slow.shift(straight.points[int(0.7*straight.get_num_points())])
|
||||
slow.shift(0.5*DOWN)
|
||||
too_long = TextMobject("Too long")
|
||||
too_long.shift(UP)
|
||||
air = TextMobject("Air").shift(2*UP)
|
||||
water = TextMobject("Water").shift(2*DOWN)
|
||||
|
||||
self.add(glass)
|
||||
self.play(GrowFromCenter(air))
|
||||
self.play(GrowFromCenter(water))
|
||||
self.dither()
|
||||
self.remove(air, water)
|
||||
ShowMultiplePathsScene.construct(self)
|
||||
self.play(
|
||||
Transform(self.path, straight)
|
||||
)
|
||||
self.dither()
|
||||
self.play(GrowFromCenter(slow))
|
||||
self.dither()
|
||||
self.remove(slow)
|
||||
self.leftmost.ingest_submobjects()
|
||||
self.play(Transform(self.path, self.leftmost, run_time = 3))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(too_long))
|
||||
self.dither()
|
||||
|
||||
|
||||
def generate_start_and_end_points(self):
|
||||
self.start_point = 3*LEFT + 2*UP
|
||||
self.end_point = 3*RIGHT + 2*DOWN
|
||||
|
||||
def get_paths(self):
|
||||
self.leftmost, self.rightmost = result = [
|
||||
Mobject(
|
||||
Line(self.start_point, midpoint),
|
||||
Line(midpoint, self.end_point)
|
||||
).highlight(color)
|
||||
for midpoint, color in zip(
|
||||
[3*LEFT, 3*RIGHT],
|
||||
Color(YELLOW).range_to(WHITE, 2)
|
||||
)
|
||||
]
|
||||
return result
|
||||
|
||||
|
||||
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(self.photon_run_along_path(
|
||||
squaggle, run_time = 2, rate_func = None
|
||||
))
|
||||
self.play(Transform(
|
||||
squaggle, line,
|
||||
path_func = path_along_arc(np.pi)
|
||||
))
|
||||
self.play(self.photon_run_along_path(line, rate_func = None))
|
||||
self.dither()
|
||||
|
||||
|
||||
def get_paths(self):
|
||||
squaggle = ParametricFunction(
|
||||
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 PhtonBendsInWater(PhotonScene, ZoomedScene):
|
||||
def construct(self):
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
kwargs = {
|
||||
"density" : self.zoom_factor*DEFAULT_POINT_DENSITY_1D
|
||||
}
|
||||
top_line = Line(SPACE_HEIGHT*UP+2*LEFT, ORIGIN, **kwargs)
|
||||
extension = Line(ORIGIN, SPACE_HEIGHT*DOWN+2*RIGHT, **kwargs)
|
||||
bottom_line = Line(ORIGIN, SPACE_HEIGHT*DOWN+RIGHT, **kwargs)
|
||||
path1 = Mobject(top_line, extension)
|
||||
path2 = Mobject(top_line, bottom_line)
|
||||
for mob in path1, path2:
|
||||
mob.ingest_submobjects()
|
||||
extension.highlight(RED)
|
||||
theta1 = np.arctan(bottom_line.get_slope())
|
||||
theta2 = np.arctan(extension.get_slope())
|
||||
arc = Arc(theta2-theta1, start_angle = theta1, radius = 2)
|
||||
question_mark = TextMobject("$\\theta$?")
|
||||
question_mark.shift(arc.get_center()+0.5*DOWN+0.25*RIGHT)
|
||||
wave = self.wavify(path2)
|
||||
wave.highlight(YELLOW)
|
||||
wave.scale(0.5)
|
||||
|
||||
self.add(glass)
|
||||
self.play(ShowCreation(path1))
|
||||
self.play(Transform(path1, path2))
|
||||
self.dither()
|
||||
# self.activate_zooming()
|
||||
self.dither()
|
||||
self.play(ShowPassingFlash(
|
||||
wave, run_time = 3, rate_func = None
|
||||
))
|
||||
self.dither()
|
||||
self.play(ShowCreation(extension))
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
ShimmerIn(question_mark)
|
||||
)
|
||||
|
||||
class LightIsFasterInAirThanWater(ShowMultiplePathsInWater):
|
||||
def construct(self):
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
equation = TexMobject("v_{\\text{air}} > v_{\\text{water}}")
|
||||
equation.to_edge(UP)
|
||||
path = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT)
|
||||
path1 = path.copy().shift(2*UP)
|
||||
path2 = path.copy().shift(2*DOWN)
|
||||
|
||||
self.add(glass)
|
||||
self.play(ShimmerIn(equation))
|
||||
self.dither()
|
||||
photon_runs = []
|
||||
photon_runs.append(self.photon_run_along_path(
|
||||
path1, rate_func = lambda t : min(1, 1.2*t)
|
||||
))
|
||||
photon_runs.append(self.photon_run_along_path(path2))
|
||||
self.play(*photon_runs, **{"run_time" : 2})
|
||||
self.dither()
|
||||
|
||||
|
||||
class GeometryOfGlassSituation(ShowMultiplePathsInWater):
|
||||
def construct(self):
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
self.generate_start_and_end_points()
|
||||
left = self.start_point[0]*RIGHT
|
||||
right = self.end_point[0]*RIGHT
|
||||
start_x = interpolate(left, right, 0.2)
|
||||
end_x = interpolate(left, right, 1.0)
|
||||
left_line = Line(self.start_point, left, color = RED_D)
|
||||
right_line = Line(self.end_point, right, color = RED_D)
|
||||
h_1, h_2 = map(TexMobject, ["h_1", "h_2"])
|
||||
h_1.next_to(left_line, LEFT)
|
||||
h_2.next_to(right_line, RIGHT)
|
||||
point_a = Dot(self.start_point)
|
||||
point_b = Dot(self.end_point)
|
||||
A = TextMobject("A").next_to(point_a, UP)
|
||||
B = TextMobject("B").next_to(point_b, DOWN)
|
||||
|
||||
x = start_x
|
||||
left_brace = Brace(Mobject(Point(left), Point(x)))
|
||||
right_brace = Brace(Mobject(Point(x), Point(right)), UP)
|
||||
x_mob = TexMobject("x")
|
||||
x_mob.next_to(left_brace, DOWN)
|
||||
w_minus_x = TexMobject("w-x")
|
||||
w_minus_x.next_to(right_brace, UP)
|
||||
top_line = Line(self.start_point, x)
|
||||
bottom_line = Line(x, self.end_point)
|
||||
top_dist = TexMobject("\\sqrt{h_1^2+x^2}")
|
||||
top_dist.scale(0.5)
|
||||
a = 0.3
|
||||
n = top_line.get_num_points()
|
||||
point = top_line.points[int(a*n)]
|
||||
top_dist.next_to(Point(point), RIGHT, buff = 0.3)
|
||||
bottom_dist = TexMobject("\\sqrt{h_2^2+(w-x)^2}")
|
||||
bottom_dist.scale(0.5)
|
||||
n = bottom_line.get_num_points()
|
||||
point = bottom_line.points[int((1-a)*n)]
|
||||
bottom_dist.next_to(Point(point), LEFT, buff = 1)
|
||||
|
||||
end_top_line = Line(self.start_point, end_x)
|
||||
end_bottom_line = Line(end_x, self.end_point)
|
||||
end_brace = Brace(Mobject(Point(left), Point(end_x)))
|
||||
end_x_mob = TexMobject("x").next_to(end_brace, DOWN)
|
||||
|
||||
axes = Mobject(
|
||||
NumberLine(),
|
||||
NumberLine().rotate(np.pi/2).shift(7*LEFT)
|
||||
)
|
||||
graph = FunctionGraph(
|
||||
lambda x : 0.4*(x+1)*(x-3)+4,
|
||||
x_min = -2,
|
||||
x_max = 4
|
||||
)
|
||||
graph.highlight(YELLOW)
|
||||
Mobject(axes, graph).scale(0.2).to_corner(UP+RIGHT, buff = 1)
|
||||
axes.add(TexMobject("x", size = "\\small").next_to(axes, RIGHT))
|
||||
axes.add(TextMobject("Travel time", size = "\\small").next_to(
|
||||
axes, UP
|
||||
))
|
||||
new_graph = graph.copy()
|
||||
midpoint = new_graph.points[new_graph.get_num_points()/2]
|
||||
new_graph.filter_out(lambda p : p[0] < midpoint[0])
|
||||
new_graph.reverse_points()
|
||||
pairs_for_end_transform = [
|
||||
(mob, mob.copy())
|
||||
for mob in top_line, bottom_line, left_brace, x_mob
|
||||
]
|
||||
|
||||
self.add(glass, point_a, point_b, A, B)
|
||||
line = Mobject(top_line, bottom_line).ingest_submobjects()
|
||||
self.play(ShowCreation(line))
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(left_brace),
|
||||
GrowFromCenter(x_mob)
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(right_brace),
|
||||
GrowFromCenter(w_minus_x)
|
||||
)
|
||||
self.play(ShowCreation(left_line), ShimmerIn(h_1))
|
||||
self.play(ShowCreation(right_line), GrowFromCenter(h_2))
|
||||
self.play(ShimmerIn(top_dist))
|
||||
self.play(GrowFromCenter(bottom_dist))
|
||||
self.dither(3)
|
||||
self.clear()
|
||||
self.add(glass, point_a, point_b, A, B,
|
||||
top_line, bottom_line, left_brace, x_mob)
|
||||
self.play(ShowCreation(axes))
|
||||
kwargs = {
|
||||
"run_time" : 4,
|
||||
}
|
||||
self.play(*[
|
||||
Transform(*pair, **kwargs)
|
||||
for pair in [
|
||||
(top_line, end_top_line),
|
||||
(bottom_line, end_bottom_line),
|
||||
(left_brace, end_brace),
|
||||
(x_mob, end_x_mob)
|
||||
]
|
||||
]+[ShowCreation(graph, **kwargs)])
|
||||
self.dither()
|
||||
self.show_derivatives(graph)
|
||||
line = self.show_derivatives(new_graph)
|
||||
self.add(line)
|
||||
self.play(*[
|
||||
Transform(*pair, rate_func = lambda x : 0.3*smooth(x))
|
||||
for pair in pairs_for_end_transform
|
||||
])
|
||||
self.dither()
|
||||
|
||||
def show_derivatives(self, graph, run_time = 2):
|
||||
step = self.frame_duration/run_time
|
||||
for a in smooth(np.arange(0, 1-step, step)):
|
||||
index = int(a*graph.get_num_points())
|
||||
p1, p2 = graph.points[index], graph.points[index+1]
|
||||
line = Line(LEFT, RIGHT)
|
||||
line.rotate(angle_of_vector(p2-p1))
|
||||
line.shift(p1)
|
||||
self.add(line)
|
||||
self.dither(self.frame_duration)
|
||||
self.remove(line)
|
||||
return line
|
||||
|
||||
|
||||
class Spring(Line):
|
||||
CONFIG = {
|
||||
"num_loops" : 5,
|
||||
"loop_radius" : 0.3,
|
||||
"color" : GREY
|
||||
}
|
||||
|
||||
def generate_points(self):
|
||||
## self.start, self.end
|
||||
length = np.linalg.norm(self.end-self.start)
|
||||
angle = angle_of_vector(self.end-self.start)
|
||||
micro_radius = self.loop_radius/length
|
||||
m = 2*np.pi*(self.num_loops+0.5)
|
||||
def loop(t):
|
||||
return micro_radius*(
|
||||
RIGHT + np.cos(m*t)*LEFT + np.sin(m*t)*UP
|
||||
)
|
||||
new_epsilon = self.epsilon/(m*micro_radius)/length
|
||||
|
||||
self.add_points([
|
||||
t*RIGHT + loop(t)
|
||||
for t in np.arange(0, 1, new_epsilon)
|
||||
])
|
||||
self.scale(length/(1+2*micro_radius))
|
||||
self.rotate(angle)
|
||||
self.shift(self.start)
|
||||
|
||||
|
||||
class SpringSetup(ShowMultiplePathsInWater):
|
||||
def construct(self):
|
||||
self.ring_shift_val = 6*RIGHT
|
||||
self.slide_kwargs = {
|
||||
"rate_func" : there_and_back,
|
||||
"run_time" : 5
|
||||
}
|
||||
|
||||
self.setup_background()
|
||||
rod = Region(
|
||||
lambda x, y : (abs(x) < 5) & (abs(y) < 0.05),
|
||||
color = GOLD_E
|
||||
)
|
||||
ring = Arc(
|
||||
angle = 11*np.pi/6,
|
||||
start_angle = -11*np.pi/12,
|
||||
radius = 0.2,
|
||||
color = YELLOW
|
||||
)
|
||||
ring.shift(-self.ring_shift_val/2)
|
||||
self.generate_springs(ring)
|
||||
|
||||
|
||||
self.add_rod_and_ring(rod, ring)
|
||||
self.slide_ring(ring)
|
||||
self.dither()
|
||||
self.add_springs()
|
||||
self.add_force_definitions()
|
||||
self.slide_system(ring)
|
||||
self.show_horizontal_component(ring)
|
||||
self.show_angles(ring)
|
||||
self.show_equation()
|
||||
|
||||
|
||||
def setup_background(self):
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
self.generate_start_and_end_points()
|
||||
point_a = Dot(self.start_point)
|
||||
point_b = Dot(self.end_point)
|
||||
A = TextMobject("A").next_to(point_a, UP)
|
||||
B = TextMobject("B").next_to(point_b, DOWN)
|
||||
self.add(glass, point_a, point_b, A, B)
|
||||
|
||||
def generate_springs(self, ring):
|
||||
self.start_springs, self.end_springs = [
|
||||
Mobject(
|
||||
Spring(self.start_point, r.get_top()),
|
||||
Spring(self.end_point, r.get_bottom())
|
||||
)
|
||||
for r in ring, ring.copy().shift(self.ring_shift_val)
|
||||
]
|
||||
|
||||
def add_rod_and_ring(self, rod, ring):
|
||||
rod_word = TextMobject("Rod")
|
||||
rod_word.next_to(Point(), UP)
|
||||
ring_word = TextMobject("Ring")
|
||||
ring_word.next_to(ring, UP)
|
||||
self.dither()
|
||||
self.add(rod)
|
||||
self.play(ShimmerIn(rod_word))
|
||||
self.dither()
|
||||
self.remove(rod_word)
|
||||
self.play(ShowCreation(ring))
|
||||
self.play(ShimmerIn(ring_word))
|
||||
self.dither()
|
||||
self.remove(ring_word)
|
||||
|
||||
def slide_ring(self, ring):
|
||||
self.play(ApplyMethod(
|
||||
ring.shift, self.ring_shift_val,
|
||||
**self.slide_kwargs
|
||||
))
|
||||
|
||||
def add_springs(self):
|
||||
colors = iter([BLACK, BLUE_E])
|
||||
for spring in self.start_springs.split():
|
||||
circle = Circle(color = colors.next())
|
||||
circle.reverse_points()
|
||||
circle.scale(spring.loop_radius)
|
||||
circle.shift(spring.points[0])
|
||||
|
||||
self.play(Transform(circle, spring))
|
||||
self.remove(circle)
|
||||
self.add(spring)
|
||||
self.dither()
|
||||
|
||||
def add_force_definitions(self):
|
||||
top_force = TexMobject("F_1 = \\dfrac{1}{v_{\\text{air}}}")
|
||||
bottom_force = TexMobject("F_2 = \\dfrac{1}{v_{\\text{water}}}")
|
||||
top_spring, bottom_spring = self.start_springs.split()
|
||||
top_force.next_to(top_spring)
|
||||
bottom_force.next_to(bottom_spring, DOWN, buff = -0.5)
|
||||
words = TextMobject("""
|
||||
The force in a real spring is
|
||||
proportional to that spring's length
|
||||
""")
|
||||
words.to_corner(UP+RIGHT)
|
||||
for force in top_force, bottom_force:
|
||||
self.play(GrowFromCenter(force))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither(3)
|
||||
self.remove(top_force, bottom_force, words)
|
||||
|
||||
def slide_system(self, ring):
|
||||
equilibrium_slide_kwargs = dict(self.slide_kwargs)
|
||||
def jiggle_to_equilibrium(t):
|
||||
return 0.7*(1+((1-t)**2)*(-np.cos(10*np.pi*t)))
|
||||
equilibrium_slide_kwargs = {
|
||||
"rate_func" : jiggle_to_equilibrium,
|
||||
"run_time" : 3
|
||||
}
|
||||
start = Mobject(ring, self.start_springs)
|
||||
end = Mobject(
|
||||
ring.copy().shift(self.ring_shift_val),
|
||||
self.end_springs
|
||||
)
|
||||
for kwargs in self.slide_kwargs, equilibrium_slide_kwargs:
|
||||
self.play(Transform(start, end, **kwargs))
|
||||
self.dither()
|
||||
|
||||
def show_horizontal_component(self, ring):
|
||||
v_right = Vector(ring.get_top(), RIGHT)
|
||||
v_left = Vector(ring.get_bottom(), LEFT)
|
||||
self.play(*map(ShowCreation, [v_right, v_left]))
|
||||
self.dither()
|
||||
self.remove(v_right, v_left)
|
||||
|
||||
def show_angles(self, ring):
|
||||
ring_center = ring.get_center()
|
||||
lines, arcs, thetas = [], [], []
|
||||
counter = it.count(1)
|
||||
for point in self.start_point, self.end_point:
|
||||
line = Line(point, ring_center, color = GREY)
|
||||
angle = np.pi/2-np.abs(np.arctan(line.get_slope()))
|
||||
arc = Arc(angle, radius = 0.5).rotate(np.pi/2)
|
||||
if point is self.end_point:
|
||||
arc.rotate(np.pi)
|
||||
theta = TexMobject("\\theta_%d"%counter.next())
|
||||
theta.scale(0.5)
|
||||
theta.shift(2*arc.get_center())
|
||||
arc.shift(ring_center)
|
||||
theta.shift(ring_center)
|
||||
|
||||
lines.append(line)
|
||||
arcs.append(arc)
|
||||
thetas.append(theta)
|
||||
vert_line = Line(2*UP, 2*DOWN)
|
||||
vert_line.shift(ring_center)
|
||||
top_spring, bottom_spring = self.start_springs.split()
|
||||
|
||||
self.play(
|
||||
Transform(ring, Point(ring_center)),
|
||||
Transform(top_spring, lines[0]),
|
||||
Transform(bottom_spring, lines[1])
|
||||
)
|
||||
self.play(ShowCreation(vert_line))
|
||||
anims = []
|
||||
for arc, theta in zip(arcs, thetas):
|
||||
anims += [
|
||||
ShowCreation(arc),
|
||||
GrowFromCenter(theta)
|
||||
]
|
||||
self.play(*anims)
|
||||
self.dither()
|
||||
|
||||
def show_equation(self):
|
||||
equation = TexMobject([
|
||||
"\\left(\\dfrac{1}{\\phantom{v_air}}\\right)",
|
||||
"\\sin(\\theta_1)",
|
||||
"=",
|
||||
"\\left(\\dfrac{1}{\\phantom{v_water}}\\right)",
|
||||
"\\sin(\\theta_2)"
|
||||
])
|
||||
equation.to_corner(UP+RIGHT)
|
||||
frac1, sin1, equals, frac2, sin2 = equation.split()
|
||||
v_air, v_water = [
|
||||
TexMobject("v_{\\text{%s}}"%s, size = "\\Large")
|
||||
for s in "air", "water"
|
||||
]
|
||||
v_air.next_to(Point(frac1.get_center()), DOWN)
|
||||
v_water.next_to(Point(frac2.get_center()), DOWN)
|
||||
frac1.add(v_air)
|
||||
frac2.add(v_water)
|
||||
f1, f2 = [
|
||||
TexMobject("F_%d"%d, size = "\\Large")
|
||||
for d in 1, 2
|
||||
]
|
||||
f1.next_to(sin1, LEFT)
|
||||
f2.next_to(equals, RIGHT)
|
||||
sin2_start = sin2.copy().next_to(f2, RIGHT)
|
||||
bar1 = TexMobject("\\dfrac{\\qquad}{\\qquad}")
|
||||
bar2 = bar1.copy()
|
||||
bar1.next_to(sin1, DOWN)
|
||||
bar2.next_to(sin2, DOWN)
|
||||
v_air_copy = v_air.copy().next_to(bar1, DOWN)
|
||||
v_water_copy = v_water.copy().next_to(bar2, DOWN)
|
||||
bars = Mobject(bar1, bar2)
|
||||
new_eq = equals.copy().center().shift(bars.get_center())
|
||||
snells = TextMobject("Snell's Law")
|
||||
snells.highlight(YELLOW)
|
||||
snells.shift(new_eq.get_center()[0]*RIGHT)
|
||||
snells.shift(UP)
|
||||
|
||||
anims = []
|
||||
for mob in f1, sin1, equals, f2, sin2_start:
|
||||
anims.append(ShimmerIn(mob))
|
||||
self.play(*anims)
|
||||
self.dither()
|
||||
for f, frac in (f1, frac1), (f2, frac2):
|
||||
target = frac.copy().ingest_submobjects()
|
||||
also = []
|
||||
if f is f2:
|
||||
also.append(Transform(sin2_start, sin2))
|
||||
sin2 = sin2_start
|
||||
self.play(Transform(f, target), *also)
|
||||
self.remove(f)
|
||||
self.add(frac)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(frac1),
|
||||
FadeOut(frac2),
|
||||
Transform(v_air, v_air_copy),
|
||||
Transform(v_water, v_water_copy),
|
||||
ShowCreation(bars),
|
||||
Transform(equals, new_eq)
|
||||
)
|
||||
self.dither()
|
||||
frac1 = Mobject(sin1, bar1, v_air)
|
||||
frac2 = Mobject(sin2, bar2, v_water)
|
||||
for frac, vect in (frac1, LEFT), (frac2, RIGHT):
|
||||
self.play(ApplyMethod(
|
||||
frac.next_to, equals, vect
|
||||
))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(snells))
|
||||
self.dither()
|
||||
|
||||
class WhatGovernsTheSpeedOfLight(PhotonScene, PathSlidingScene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.scale(RANDY_SCALE_VAL)
|
||||
randy.shift(-randy.get_bottom())
|
||||
self.add_cycloid_end_points()
|
||||
|
||||
self.add(self.cycloid)
|
||||
self.slide(randy, self.cycloid)
|
||||
self.play(self.photon_run_along_path(self.cycloid))
|
||||
|
||||
self.dither()
|
||||
|
||||
class WhichPathWouldLightTake(PhotonScene, TryManyPaths):
|
||||
def construct(self):
|
||||
words = TextMobject(
|
||||
["Which path ", "would \\emph{light} take", "?"]
|
||||
)
|
||||
words.split()[1].highlight(YELLOW)
|
||||
words.to_corner(UP+RIGHT)
|
||||
self.add_cycloid_end_points()
|
||||
|
||||
anims = [
|
||||
self.photon_run_along_path(
|
||||
path,
|
||||
rate_func = smooth
|
||||
)
|
||||
for path in self.get_paths()
|
||||
]
|
||||
self.play(anims[0], ShimmerIn(words))
|
||||
for anim in anims[1:]:
|
||||
self.play(anim)
|
||||
|
||||
|
||||
|
||||
class StateSnellsLaw(PhotonScene):
|
||||
def construct(self):
|
||||
point_a = 3*LEFT+3*UP
|
||||
point_b = 1.5*RIGHT+3*DOWN
|
||||
midpoint = ORIGIN
|
||||
|
||||
lines, arcs, thetas = [], [], []
|
||||
counter = it.count(1)
|
||||
for point in point_a, point_b:
|
||||
line = Line(point, midpoint, color = RED_D)
|
||||
angle = np.pi/2-np.abs(np.arctan(line.get_slope()))
|
||||
arc = Arc(angle, radius = 0.5).rotate(np.pi/2)
|
||||
if point is point_b:
|
||||
arc.rotate(np.pi)
|
||||
line.reverse_points()
|
||||
theta = TexMobject("\\theta_%d"%counter.next())
|
||||
theta.scale(0.5)
|
||||
theta.shift(2*arc.get_center())
|
||||
arc.shift(midpoint)
|
||||
theta.shift(midpoint)
|
||||
|
||||
lines.append(line)
|
||||
arcs.append(arc)
|
||||
thetas.append(theta)
|
||||
vert_line = Line(2*UP, 2*DOWN)
|
||||
vert_line.shift(midpoint)
|
||||
path = Mobject(*lines).ingest_submobjects()
|
||||
glass = Region(lambda x, y : y < 0, color = BLUE_E)
|
||||
self.add(glass)
|
||||
equation = TexMobject([
|
||||
"\\dfrac{\\sin(\\theta_1)}{v_{\\text{air}}}",
|
||||
"=",
|
||||
"\\dfrac{\\sin(\\theta_2)}{v_{\\text{water}}}",
|
||||
])
|
||||
equation.to_corner(UP+RIGHT)
|
||||
exp1, equals, exp2 = equation.split()
|
||||
snells_law = TextMobject("Snell's Law:")
|
||||
snells_law.highlight(YELLOW)
|
||||
snells_law.to_edge(UP)
|
||||
|
||||
self.play(ShimmerIn(snells_law))
|
||||
self.dither()
|
||||
self.play(ShowCreation(path))
|
||||
self.play(self.photon_run_along_path(path))
|
||||
self.dither()
|
||||
self.play(ShowCreation(vert_line))
|
||||
self.play(*map(ShowCreation, arcs))
|
||||
self.play(*map(GrowFromCenter, thetas))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(exp1))
|
||||
self.dither()
|
||||
self.play(*map(ShimmerIn, [equals, exp2]))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
512
old_projects/brachistochrone/misc.py
Normal file
512
old_projects/brachistochrone/misc.py
Normal file
@ -0,0 +1,512 @@
|
||||
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 *
|
||||
from animation.simple_animations import *
|
||||
from animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import *
|
||||
from topics.characters import *
|
||||
from topics.functions import ParametricFunction, FunctionGraph
|
||||
from topics.number_line import *
|
||||
from mobject.region import Region, region_from_polygon_vertices
|
||||
from topics.three_dimensions import Stars
|
||||
from scene import Scene
|
||||
|
||||
from brachistochrone.curves import Cycloid
|
||||
|
||||
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_submobjects()
|
||||
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
|
||||
))
|
||||
|
||||
|
||||
|
||||
class TimeLine(Scene):
|
||||
def construct(self):
|
||||
dated_events = [
|
||||
{
|
||||
"date" : 1696,
|
||||
"text": "Johann Bernoulli poses Brachistochrone problem",
|
||||
"picture" : "Johann_Bernoulli2"
|
||||
},
|
||||
{
|
||||
"date" : 1662,
|
||||
"text" : "Fermat states his principle of least time",
|
||||
"picture" : "Pierre_de_Fermat"
|
||||
}
|
||||
]
|
||||
speical_dates = [2016] + [
|
||||
obj["date"] for obj in dated_events
|
||||
]
|
||||
centuries = range(1600, 2100, 100)
|
||||
timeline = NumberLine(
|
||||
numerical_radius = 300,
|
||||
number_at_center = 1800,
|
||||
unit_length_to_spatial_width = SPACE_WIDTH/100,
|
||||
tick_frequency = 10,
|
||||
numbers_with_elongated_ticks = centuries
|
||||
)
|
||||
timeline.add_numbers(*centuries)
|
||||
centers = [
|
||||
Point(timeline.number_to_point(year))
|
||||
for year in speical_dates
|
||||
]
|
||||
timeline.add(*centers)
|
||||
timeline.shift(-centers[0].get_center())
|
||||
|
||||
self.add(timeline)
|
||||
self.dither()
|
||||
run_times = iter([3, 1])
|
||||
for point, event in zip(centers[1:], dated_events):
|
||||
self.play(ApplyMethod(
|
||||
timeline.shift, -point.get_center(),
|
||||
run_time = run_times.next()
|
||||
))
|
||||
picture = ImageMobject(event["picture"], invert = False)
|
||||
picture.scale_to_fit_width(2)
|
||||
picture.to_corner(UP+RIGHT)
|
||||
event_mob = TextMobject(event["text"])
|
||||
event_mob.shift(2*LEFT+2*UP)
|
||||
date_mob = TexMobject(str(event["date"]))
|
||||
date_mob.scale(0.5)
|
||||
date_mob.shift(0.6*UP)
|
||||
line = Line(event_mob.get_bottom(), 0.2*UP)
|
||||
self.play(
|
||||
ShimmerIn(event_mob),
|
||||
ShowCreation(line),
|
||||
ShimmerIn(date_mob)
|
||||
)
|
||||
self.play(FadeIn(picture))
|
||||
self.dither(3)
|
||||
self.play(*map(FadeOut, [event_mob, date_mob, line, picture]))
|
||||
|
||||
|
||||
class StayedUpAllNight(Scene):
|
||||
def construct(self):
|
||||
clock = Circle(radius = 2, color = WHITE)
|
||||
clock.add(Dot(ORIGIN))
|
||||
ticks = Mobject(*[
|
||||
Line(1.8*vect, 2*vect, color = GREY)
|
||||
for vect in compass_directions(12)
|
||||
])
|
||||
clock.add(ticks)
|
||||
hour_hand = Line(ORIGIN, UP)
|
||||
minute_hand = Line(ORIGIN, 1.5*UP)
|
||||
clock.add(hour_hand, minute_hand)
|
||||
clock.to_corner(UP+RIGHT)
|
||||
hour_hand.get_center = lambda : clock.get_center()
|
||||
minute_hand.get_center = lambda : clock.get_center()
|
||||
|
||||
solution = ImageMobject(
|
||||
"Newton_brachistochrone_solution2",
|
||||
use_cache = False
|
||||
)
|
||||
solution.stroke_width = 3
|
||||
solution.highlight(GREY)
|
||||
solution.scale_to_fit_width(5)
|
||||
solution.to_corner(UP+RIGHT)
|
||||
newton = ImageMobject("Old_Newton", invert = False)
|
||||
newton.scale(0.8)
|
||||
phil_trans = TextMobject("Philosophical Transactions")
|
||||
rect = Rectangle(height = 6, width = 4.5, color = WHITE)
|
||||
rect.to_corner(UP+RIGHT)
|
||||
rect.shift(DOWN)
|
||||
phil_trans.scale_to_fit_width(0.8*rect.get_width())
|
||||
phil_trans.next_to(Point(rect.get_top()), DOWN)
|
||||
new_solution = solution.copy()
|
||||
new_solution.scale_to_fit_width(phil_trans.get_width())
|
||||
new_solution.next_to(phil_trans, DOWN, buff = 1)
|
||||
not_newton = TextMobject("-Totally not by Newton")
|
||||
not_newton.scale_to_fit_width(2.5)
|
||||
not_newton.next_to(new_solution, DOWN, aligned_edge = RIGHT)
|
||||
phil_trans.add(rect)
|
||||
|
||||
newton_complaint = TextMobject([
|
||||
"``I do not love to be",
|
||||
" \\emph{dunned} ",
|
||||
"and teased by foreigners''"
|
||||
], size = "\\small")
|
||||
newton_complaint.to_edge(UP, buff = 0.2)
|
||||
dunned = newton_complaint.split()[1]
|
||||
dunned.highlight()
|
||||
dunned_def = TextMobject("(old timey term for making \\\\ demands on someone)")
|
||||
dunned_def.scale(0.7)
|
||||
dunned_def.next_to(phil_trans, LEFT)
|
||||
dunned_def.shift(2*UP)
|
||||
dunned_arrow = Arrow(dunned_def, dunned)
|
||||
|
||||
johann = ImageMobject("Johann_Bernoulli2", invert = False)
|
||||
johann.scale(0.4)
|
||||
johann.to_edge(LEFT)
|
||||
johann.shift(DOWN)
|
||||
johann_quote = TextMobject("``I recognize the lion by his claw''")
|
||||
johann_quote.next_to(johann, UP, aligned_edge = LEFT)
|
||||
|
||||
self.play(ApplyMethod(newton.to_edge, LEFT))
|
||||
self.play(ShowCreation(clock))
|
||||
kwargs = {
|
||||
"axis" : OUT,
|
||||
"rate_func" : smooth
|
||||
}
|
||||
self.play(
|
||||
Rotating(hour_hand, radians = -2*np.pi, **kwargs),
|
||||
Rotating(minute_hand, radians = -12*2*np.pi, **kwargs),
|
||||
run_time = 5
|
||||
)
|
||||
self.dither()
|
||||
self.clear()
|
||||
self.add(newton)
|
||||
clock.ingest_submobjects()
|
||||
self.play(Transform(clock, solution))
|
||||
self.remove(clock)
|
||||
self.add(solution)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeIn(phil_trans),
|
||||
Transform(solution, new_solution)
|
||||
)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(not_newton))
|
||||
phil_trans.add(solution, not_newton)
|
||||
self.dither()
|
||||
self.play(*map(ShimmerIn, newton_complaint.split()))
|
||||
self.dither()
|
||||
self.play(
|
||||
ShimmerIn(dunned_def),
|
||||
ShowCreation(dunned_arrow)
|
||||
)
|
||||
self.dither()
|
||||
self.remove(dunned_def, dunned_arrow)
|
||||
self.play(FadeOut(newton_complaint))
|
||||
self.remove(newton_complaint)
|
||||
self.play(
|
||||
FadeOut(newton),
|
||||
GrowFromCenter(johann)
|
||||
)
|
||||
self.remove(newton)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(johann_quote))
|
||||
self.dither()
|
||||
|
||||
|
||||
class ThetaTGraph(Scene):
|
||||
def construct(self):
|
||||
t_axis = NumberLine()
|
||||
theta_axis = NumberLine().rotate(np.pi/2)
|
||||
theta_mob = TexMobject("\\theta(t)")
|
||||
t_mob = TexMobject("t")
|
||||
theta_mob.next_to(theta_axis, RIGHT)
|
||||
theta_mob.to_edge(UP)
|
||||
t_mob.next_to(t_axis, UP)
|
||||
t_mob.to_edge(RIGHT)
|
||||
graph = ParametricFunction(
|
||||
lambda t : 4*t*RIGHT + 2*smooth(t)*UP
|
||||
)
|
||||
line = Line(graph.points[0], graph.points[-1], color = WHITE)
|
||||
q_mark = TextMobject("?")
|
||||
q_mark.next_to(Point(graph.get_center()), LEFT)
|
||||
stars = Stars(color = BLACK)
|
||||
stars.scale(0.1).shift(q_mark.get_center())
|
||||
|
||||
|
||||
squiggle = ParametricFunction(
|
||||
lambda t : t*RIGHT + 0.2*t*(5-t)*(np.sin(t)**2)*UP,
|
||||
start = 0,
|
||||
end = 5
|
||||
)
|
||||
|
||||
self.play(
|
||||
ShowCreation(t_axis),
|
||||
ShowCreation(theta_axis),
|
||||
ShimmerIn(theta_mob),
|
||||
ShimmerIn(t_mob)
|
||||
)
|
||||
self.play(
|
||||
ShimmerIn(q_mark),
|
||||
ShowCreation(graph)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(q_mark, stars),
|
||||
Transform(graph, line)
|
||||
)
|
||||
self.dither()
|
||||
self.play(Transform(graph, squiggle))
|
||||
self.dither()
|
||||
|
||||
|
||||
class SolutionsToTheBrachistochrone(Scene):
|
||||
def construct(self):
|
||||
r_range = np.arange(0.5, 2, 0.25)
|
||||
cycloids = Mobject(*[
|
||||
Cycloid(radius = r, end_theta=2*np.pi)
|
||||
for r in r_range
|
||||
])
|
||||
lower_left = 2*DOWN+6*LEFT
|
||||
lines = Mobject(*[
|
||||
Line(
|
||||
lower_left,
|
||||
lower_left+5*r*np.cos(np.arctan(r))*RIGHT+2*r*np.sin(np.arctan(r))*UP
|
||||
)
|
||||
for r in r_range
|
||||
])
|
||||
nl = NumberLine(numbers_with_elongated_ticks = [])
|
||||
x_axis = nl.copy().shift(3*UP)
|
||||
y_axis = nl.copy().rotate(np.pi/2).shift(6*LEFT)
|
||||
t_axis = nl.copy().shift(2*DOWN)
|
||||
x_label = TexMobject("x")
|
||||
x_label.next_to(x_axis, DOWN)
|
||||
x_label.to_edge(RIGHT)
|
||||
y_label = TexMobject("y")
|
||||
y_label.next_to(y_axis, RIGHT)
|
||||
y_label.shift(2*DOWN)
|
||||
t_label = TexMobject("t")
|
||||
t_label.next_to(t_axis, UP)
|
||||
t_label.to_edge(RIGHT)
|
||||
theta_label = TexMobject("\\theta")
|
||||
theta_label.next_to(y_axis, RIGHT)
|
||||
theta_label.to_edge(UP)
|
||||
words = TextMobject("Boundary conditions?")
|
||||
words.next_to(lines, RIGHT)
|
||||
words.shift(2*UP)
|
||||
|
||||
self.play(ShowCreation(x_axis), ShimmerIn(x_label))
|
||||
self.play(ShowCreation(y_axis), ShimmerIn(y_label))
|
||||
self.play(ShowCreation(cycloids))
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(cycloids, lines),
|
||||
Transform(x_axis, t_axis),
|
||||
Transform(x_label, t_label),
|
||||
Transform(y_label, theta_label),
|
||||
run_time = 2
|
||||
)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither()
|
||||
|
||||
|
||||
class VideoLayout(Scene):
|
||||
def construct(self):
|
||||
left, right = 5*LEFT, 5*RIGHT
|
||||
top_words = TextMobject("The next 15 minutes of your life:")
|
||||
top_words.to_edge(UP)
|
||||
line = Line(left, right, color = BLUE_D)
|
||||
for a in np.arange(0, 4./3, 1./3):
|
||||
vect = interpolate(left, right, a)
|
||||
line.add_line(vect+0.2*DOWN, vect+0.2*UP)
|
||||
left_brace = Brace(
|
||||
Mobject(
|
||||
Point(left),
|
||||
Point(interpolate(left, right, 2./3))
|
||||
),
|
||||
DOWN
|
||||
)
|
||||
right_brace = Brace(
|
||||
Mobject(
|
||||
Point(interpolate(left, right, 2./3)),
|
||||
Point(right)
|
||||
),
|
||||
UP
|
||||
)
|
||||
left_brace.words = map(TextMobject, [
|
||||
"Problem statement",
|
||||
"History",
|
||||
"Johann Bernoulli's cleverness"
|
||||
])
|
||||
curr = left_brace
|
||||
right_brace.words = map(TextMobject, [
|
||||
"Challenge",
|
||||
"Mark Levi's cleverness",
|
||||
])
|
||||
for brace in left_brace, right_brace:
|
||||
curr = brace
|
||||
direction = DOWN if brace is left_brace else UP
|
||||
for word in brace.words:
|
||||
word.next_to(curr, direction)
|
||||
curr = word
|
||||
right_brace.words.reverse()
|
||||
|
||||
self.play(ShimmerIn(top_words))
|
||||
self.play(ShowCreation(line))
|
||||
for brace in left_brace, right_brace:
|
||||
self.play(GrowFromCenter(brace))
|
||||
self.dither()
|
||||
for word in brace.words:
|
||||
self.play(ShimmerIn(word))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
class ShortestPathProblem(Scene):
|
||||
def construct(self):
|
||||
point_a, point_b = 3*LEFT, 3*RIGHT
|
||||
dots = []
|
||||
for point, char in [(point_a, "A"), (point_b, "B")]:
|
||||
dot = Dot(point)
|
||||
letter = TexMobject(char)
|
||||
letter.next_to(dot, UP+LEFT)
|
||||
dot.add(letter)
|
||||
dots.append(dot)
|
||||
|
||||
path = ParametricFunction(
|
||||
lambda t : (t/2 + np.cos(t))*RIGHT + np.sin(t)*UP,
|
||||
start = -2*np.pi,
|
||||
end = 2*np.pi
|
||||
)
|
||||
path.scale(6/(2*np.pi))
|
||||
path.shift(point_a - path.points[0])
|
||||
path.highlight(RED)
|
||||
line = Line(point_a, point_b)
|
||||
words = TextMobject("Shortest path from $A$ to $B$")
|
||||
words.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
ShimmerIn(words),
|
||||
*map(GrowFromCenter, dots)
|
||||
)
|
||||
self.play(ShowCreation(path))
|
||||
self.play(Transform(
|
||||
path, line,
|
||||
path_func = path_along_arc(np.pi)
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
class MathBetterThanTalking(Scene):
|
||||
def construct(self):
|
||||
mathy = Mathematician()
|
||||
mathy.to_corner(DOWN+LEFT)
|
||||
bubble = ThoughtBubble()
|
||||
bubble.pin_to(mathy)
|
||||
bubble.write("Math $>$ Talking about math")
|
||||
|
||||
self.add(mathy)
|
||||
self.play(ShowCreation(bubble))
|
||||
self.play(ShimmerIn(bubble.content))
|
||||
self.dither()
|
||||
self.play(ApplyMethod(
|
||||
mathy.blink,
|
||||
rate_func = squish_rate_func(there_and_back, 0.4, 0.6)
|
||||
))
|
||||
|
||||
|
||||
class DetailsOfProofBox(Scene):
|
||||
def construct(self):
|
||||
rect = Rectangle(height = 4, width = 6, color = WHITE)
|
||||
words = TextMobject("Details of proof")
|
||||
words.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
ShowCreation(rect),
|
||||
ShimmerIn(words)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class TalkedAboutSnellsLaw(Scene):
|
||||
def construct(self):
|
||||
randy = Randolph()
|
||||
randy.to_corner(DOWN+LEFT)
|
||||
morty = Mortimer()
|
||||
morty.to_edge(DOWN+RIGHT)
|
||||
randy.bubble = SpeechBubble().pin_to(randy)
|
||||
morty.bubble = SpeechBubble().pin_to(morty)
|
||||
|
||||
phrases = [
|
||||
"Let's talk about Snell's law",
|
||||
"I love Snell's law",
|
||||
"It's like running from \\\\ a beach into the ocean",
|
||||
"It's like two constant \\\\ tension springs",
|
||||
]
|
||||
|
||||
self.add(randy, morty)
|
||||
talkers = it.cycle([randy, morty])
|
||||
for talker, phrase in zip(talkers, phrases):
|
||||
talker.bubble.write(phrase)
|
||||
self.play(
|
||||
FadeIn(talker.bubble),
|
||||
ShimmerIn(talker.bubble.content)
|
||||
)
|
||||
self.play(ApplyMethod(
|
||||
talker.blink,
|
||||
rate_func = squish_rate_func(there_and_back)
|
||||
))
|
||||
self.dither()
|
||||
self.remove(talker.bubble, talker.bubble.content)
|
||||
|
||||
|
||||
class YetAnotherMarkLevi(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("Yet another bit of Mark Levi cleverness")
|
||||
words.to_edge(UP)
|
||||
levi = ImageMobject("Mark_Levi", invert = False)
|
||||
levi.scale_to_fit_width(6)
|
||||
levi.show()
|
||||
|
||||
self.add(levi)
|
||||
self.play(ShimmerIn(words))
|
||||
self.dither(2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
498
old_projects/brachistochrone/multilayered.py
Normal file
498
old_projects/brachistochrone/multilayered.py
Normal file
@ -0,0 +1,498 @@
|
||||
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 *
|
||||
|
||||
|
||||
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 ShowDiscretePath(MultilayeredScene, PhotonScene):
|
||||
CONFIG = {
|
||||
"RectClass" : FilledRectangle
|
||||
}
|
||||
def construct(self):
|
||||
self.add_layers()
|
||||
self.cycloid = Cycloid(end_theta = np.pi)
|
||||
|
||||
self.generate_discrete_path()
|
||||
self.play(ShowCreation(self.discrete_path))
|
||||
self.dither()
|
||||
self.play(self.photon_run_along_path(
|
||||
self.discrete_path,
|
||||
rate_func = rush_into,
|
||||
run_time = 3
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
def generate_discrete_path(self):
|
||||
points = self.cycloid.points
|
||||
tops = [mob.get_top()[1] for mob in self.layers]
|
||||
tops.append(tops[-1]-self.layers[0].get_height())
|
||||
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 = WHITE,
|
||||
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], SPACE_WIDTH*RIGHT+(tops[-1]-0.5)*UP
|
||||
)
|
||||
|
||||
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 = [
|
||||
Mobject(*[
|
||||
Mobject(
|
||||
*layer_sets[x][(2**x)*index:(2**x)*(index+1)]
|
||||
)
|
||||
for index in range(self.n_layers)
|
||||
]).ingest_submobjects()
|
||||
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]
|
||||
self.add(curr_set)
|
||||
for layer_set in glass_sets[1:]:
|
||||
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.8))
|
||||
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(1.2*t, 2),
|
||||
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):
|
||||
CONFIG = {
|
||||
"arc_radius" : 0.5,
|
||||
"RectClass" : FilledRectangle
|
||||
}
|
||||
def construct(self):
|
||||
glass = self.get_continuous_glass()
|
||||
self.add(glass)
|
||||
self.freeze_background()
|
||||
|
||||
cycloid = Cycloid(end_theta = np.pi)
|
||||
cycloid.highlight(YELLOW)
|
||||
chopped_cycloid = cycloid.copy()
|
||||
n = cycloid.get_num_points()
|
||||
chopped_cycloid.filter_out(lambda p : p[1] > 1 and p[0] < 0)
|
||||
chopped_cycloid.reverse_points()
|
||||
|
||||
|
||||
self.play(ShowCreation(cycloid))
|
||||
ref_mob = self.snells_law_at_every_point(cycloid, chopped_cycloid)
|
||||
self.show_equation(chopped_cycloid, ref_mob)
|
||||
|
||||
def snells_law_at_every_point(self, cycloid, chopped_cycloid):
|
||||
square = Square(side_length = 0.2, color = WHITE)
|
||||
words = TextMobject(["Snell's law ", "everywhere"])
|
||||
snells, rest = words.split()
|
||||
colon = TextMobject(":")
|
||||
words.next_to(square)
|
||||
words.shift(0.3*UP)
|
||||
combo = Mobject(square, words)
|
||||
combo.get_center = lambda : square.get_center()
|
||||
new_snells = snells.copy().center().to_edge(UP, buff = 1.5)
|
||||
colon.next_to(new_snells)
|
||||
colon.shift(0.05*DOWN)
|
||||
|
||||
self.play(MoveAlongPath(
|
||||
combo, cycloid,
|
||||
run_time = 5
|
||||
))
|
||||
self.play(MoveAlongPath(
|
||||
combo, chopped_cycloid,
|
||||
run_time = 4
|
||||
))
|
||||
dot = Dot(combo.get_center())
|
||||
self.play(Transform(square, dot))
|
||||
self.play(
|
||||
Transform(snells, new_snells),
|
||||
Transform(rest, colon)
|
||||
)
|
||||
self.dither()
|
||||
return colon
|
||||
|
||||
def get_marks(self, point1, point2):
|
||||
vert_line = Line(2*DOWN, 2*UP)
|
||||
tangent_line = vert_line.copy()
|
||||
theta = TexMobject("\\theta")
|
||||
theta.scale(0.5)
|
||||
angle = angle_of_vector(point1 - point2)
|
||||
tangent_line.rotate(
|
||||
angle - tangent_line.get_angle()
|
||||
)
|
||||
angle_from_vert = angle - np.pi/2
|
||||
for mob in vert_line, tangent_line:
|
||||
mob.shift(point1 - mob.get_center())
|
||||
arc = Arc(angle_from_vert, start_angle = np.pi/2)
|
||||
arc.scale(self.arc_radius)
|
||||
arc.shift(point1)
|
||||
vect_angle = angle_from_vert/2 + np.pi/2
|
||||
vect = rotate_vector(RIGHT, vect_angle)
|
||||
theta.center()
|
||||
theta.shift(point1)
|
||||
theta.shift(1.5*self.arc_radius*vect)
|
||||
return arc, theta, vert_line, tangent_line
|
||||
|
||||
|
||||
def show_equation(self, chopped_cycloid, ref_mob):
|
||||
point2, point1 = chopped_cycloid.points[-2:]
|
||||
arc, theta, vert_line, tangent_line = self.get_marks(
|
||||
point1, point2
|
||||
)
|
||||
equation = TexMobject([
|
||||
"\\sin(\\theta)",
|
||||
"\\over \\sqrt{y}",
|
||||
])
|
||||
sin, sqrt_y = equation.split()
|
||||
equation.next_to(ref_mob)
|
||||
const = TexMobject(" = \\text{constant}")
|
||||
const.next_to(equation)
|
||||
ceil_point = np.array(point1)
|
||||
ceil_point[1] = self.top[1]
|
||||
brace = Brace(
|
||||
Mobject(Point(point1), Point(ceil_point)),
|
||||
RIGHT
|
||||
)
|
||||
y_mob = TexMobject("y").next_to(brace)
|
||||
|
||||
self.play(
|
||||
GrowFromCenter(sin),
|
||||
ShowCreation(arc),
|
||||
GrowFromCenter(theta)
|
||||
)
|
||||
self.play(ShowCreation(vert_line))
|
||||
self.play(ShowCreation(tangent_line))
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(sqrt_y),
|
||||
GrowFromCenter(brace),
|
||||
GrowFromCenter(y_mob)
|
||||
)
|
||||
self.dither()
|
||||
self.play(Transform(
|
||||
Point(const.get_left()), const
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
572
old_projects/brachistochrone/wordplay.py
Normal file
572
old_projects/brachistochrone/wordplay.py
Normal file
@ -0,0 +1,572 @@
|
||||
import numpy as np
|
||||
import itertools as it
|
||||
import os
|
||||
|
||||
from helpers import *
|
||||
|
||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||
from mobject import Mobject
|
||||
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 animation.playground import TurnInsideOut, Vibrate
|
||||
from topics.geometry import *
|
||||
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
|
||||
from generate_logo import LogoGeneration
|
||||
from brachistochrone.drawing_images import sort_by_color
|
||||
|
||||
class Intro(Scene):
|
||||
def construct(self):
|
||||
logo = ImageMobject("LogoGeneration", invert = False)
|
||||
name_mob = TextMobject("3Blue1Brown").center()
|
||||
name_mob.highlight("grey")
|
||||
name_mob.shift(2*DOWN)
|
||||
self.add(name_mob, logo)
|
||||
|
||||
new_text = TextMobject(["with ", "Steven Strogatz"])
|
||||
new_text.next_to(name_mob, DOWN)
|
||||
self.play(*[
|
||||
ShimmerIn(part)
|
||||
for part in new_text.split()
|
||||
])
|
||||
self.dither()
|
||||
with_word, steve = new_text.split()
|
||||
steve_copy = steve.copy().center().to_edge(UP)
|
||||
# logo.sort_points(lambda p : -np.linalg.norm(p))
|
||||
sort_by_color(logo)
|
||||
self.play(
|
||||
Transform(steve, steve_copy),
|
||||
DelayByOrder(Transform(logo, Point())),
|
||||
FadeOut(with_word),
|
||||
FadeOut(name_mob),
|
||||
run_time = 3
|
||||
)
|
||||
|
||||
|
||||
class IntroduceSteve(Scene):
|
||||
def construct(self):
|
||||
name = TextMobject("Steven Strogatz")
|
||||
name.to_edge(UP)
|
||||
contributions = TextMobject("Frequent Contributions")
|
||||
contributions.scale(0.5).to_edge(RIGHT).shift(2*UP)
|
||||
books_word = TextMobject("Books")
|
||||
books_word.scale(0.5).to_edge(LEFT).shift(2*UP)
|
||||
radio_lab, sci_fri, cornell, book2, book3, book4 = [
|
||||
ImageMobject(filename, invert = False, filter_color = WHITE)
|
||||
for filename in [
|
||||
"radio_lab",
|
||||
"science_friday",
|
||||
"cornell",
|
||||
"strogatz_book2",
|
||||
"strogatz_book3",
|
||||
"strogatz_book4",
|
||||
]
|
||||
]
|
||||
book1 = ImageMobject("strogatz_book1", invert = False)
|
||||
nyt = ImageMobject("new_york_times")
|
||||
logos = [radio_lab, nyt, sci_fri]
|
||||
books = [book1, book2, book3, book4]
|
||||
|
||||
sample_size = Square(side_length = 2)
|
||||
last = contributions
|
||||
for image in logos:
|
||||
image.replace(sample_size)
|
||||
image.next_to(last, DOWN)
|
||||
last = image
|
||||
sci_fri.scale_in_place(0.9)
|
||||
shift_val = 0
|
||||
sample_size.scale(0.75)
|
||||
for book in books:
|
||||
book.replace(sample_size)
|
||||
book.next_to(books_word, DOWN)
|
||||
book.shift(shift_val*(RIGHT+DOWN))
|
||||
shift_val += 0.5
|
||||
sample_size.scale(2)
|
||||
cornell.replace(sample_size)
|
||||
cornell.next_to(name, DOWN)
|
||||
|
||||
self.add(name)
|
||||
self.play(FadeIn(cornell))
|
||||
self.play(ShimmerIn(books_word))
|
||||
for book in books:
|
||||
book.shift(5*LEFT)
|
||||
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()
|
||||
|
||||
|
||||
class WhatIsTheBrachistochrone(Scene):
|
||||
def construct(self):
|
||||
self.play(ShimmerIn(TextMobject("""
|
||||
So \\dots what is the Brachistochrone?
|
||||
""")))
|
||||
self.dither()
|
||||
|
||||
|
||||
class DisectBrachistochroneWord(Scene):
|
||||
def construct(self):
|
||||
word = TextMobject(["Bra", "chis", "to", "chrone"])
|
||||
original_word = word.copy()
|
||||
dots = []
|
||||
for part in word.split():
|
||||
if dots:
|
||||
part.next_to(dots[-1], buff = 0.06)
|
||||
dot = TexMobject("\\cdot")
|
||||
dot.next_to(part, buff = 0.06)
|
||||
dots.append(dot)
|
||||
dots = Mobject(*dots[:-1])
|
||||
dots.shift(0.1*DOWN)
|
||||
Mobject(word, dots).center()
|
||||
overbrace1 = Brace(Mobject(*word.split()[:-1]), UP)
|
||||
overbrace2 = Brace(word.split()[-1], UP)
|
||||
shortest = TextMobject("Shortest")
|
||||
shortest.next_to(overbrace1, UP)
|
||||
shortest.highlight(YELLOW)
|
||||
time = TextMobject("Time")
|
||||
time.next_to(overbrace2, UP)
|
||||
time.highlight(YELLOW)
|
||||
chrono_example = TextMobject("""
|
||||
As in ``Chronological'' \\\\
|
||||
or ``Synchronize''
|
||||
""")
|
||||
chrono_example.scale(0.5)
|
||||
chrono_example.to_edge(RIGHT)
|
||||
chrono_example.shift(2*UP)
|
||||
chrono_example.highlight(BLUE_D)
|
||||
chrono_arrow = Arrow(
|
||||
word.get_right(),
|
||||
chrono_example.get_bottom(),
|
||||
color = BLUE_D
|
||||
)
|
||||
brachy_example = TextMobject("As in . . . brachydactyly?")
|
||||
brachy_example.scale(0.5)
|
||||
brachy_example.to_edge(LEFT)
|
||||
brachy_example.shift(2*DOWN)
|
||||
brachy_example.highlight(GREEN)
|
||||
brachy_arrow = Arrow(
|
||||
word.get_left(),
|
||||
brachy_example.get_top(),
|
||||
color = GREEN
|
||||
)
|
||||
|
||||
pronunciation = TextMobject(["/br", "e", "kist","e","kr$\\bar{o}$n/"])
|
||||
pronunciation.split()[1].rotate_in_place(np.pi)
|
||||
pronunciation.split()[3].rotate_in_place(np.pi)
|
||||
pronunciation.scale(0.7)
|
||||
pronunciation.shift(DOWN)
|
||||
|
||||
latin = TextMobject(list("Latin"))
|
||||
greek = TextMobject(list("Greek"))
|
||||
for mob in latin, greek:
|
||||
mob.to_edge(LEFT)
|
||||
question_mark = TextMobject("?").next_to(greek, buff = 0.1)
|
||||
stars = Stars().highlight(BLACK)
|
||||
stars.scale(0.5).shift(question_mark.get_center())
|
||||
|
||||
self.play(Transform(original_word, word), ShowCreation(dots))
|
||||
self.play(ShimmerIn(pronunciation))
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(overbrace1),
|
||||
GrowFromCenter(overbrace2)
|
||||
)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(latin))
|
||||
self.play(FadeIn(question_mark))
|
||||
self.play(Transform(
|
||||
latin, greek,
|
||||
path_func = counterclockwise_path()
|
||||
))
|
||||
self.dither()
|
||||
self.play(Transform(question_mark, stars))
|
||||
self.remove(stars)
|
||||
self.dither()
|
||||
self.play(ShimmerIn(shortest))
|
||||
self.play(ShimmerIn(time))
|
||||
for ex, ar in [(chrono_example, chrono_arrow), (brachy_example, brachy_arrow)]:
|
||||
self.play(
|
||||
ShowCreation(ar),
|
||||
ShimmerIn(ex)
|
||||
)
|
||||
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):
|
||||
def construct(self):
|
||||
words = TextMobject([
|
||||
"Fermat's principle:",
|
||||
"""
|
||||
If a beam of light travels
|
||||
from point $A$ to $B$, it does so along the
|
||||
fastest path possible.
|
||||
"""
|
||||
])
|
||||
words.split()[0].highlight(BLUE)
|
||||
everything = MobjectFromRegion(Region())
|
||||
everything.scale(0.9)
|
||||
angles = np.apply_along_axis(
|
||||
angle_of_vector, 1, everything.points
|
||||
)
|
||||
norms = np.apply_along_axis(
|
||||
np.linalg.norm, 1, everything.points
|
||||
)
|
||||
norms -= np.min(norms)
|
||||
norms /= np.max(norms)
|
||||
alphas = 0.25 + 0.75 * norms * (1 + np.sin(12*angles))/2
|
||||
everything.rgbs = alphas.repeat(3).reshape((len(alphas), 3))
|
||||
|
||||
Mobject(everything, words).show()
|
||||
|
||||
everything.sort_points(np.linalg.norm)
|
||||
self.add(words)
|
||||
self.play(
|
||||
DelayByOrder(FadeIn(everything, run_time = 3)),
|
||||
Animation(words)
|
||||
)
|
||||
self.play(
|
||||
ApplyMethod(everything.highlight, WHITE),
|
||||
)
|
||||
self.dither()
|
||||
|
||||
class VideoProgression(Scene):
|
||||
def construct(self):
|
||||
spacing = 2*UP
|
||||
brachy, optics, light_in_two, snells, multi = words = [
|
||||
TextMobject(text)
|
||||
for text in [
|
||||
"Brachistochrone",
|
||||
"Optics",
|
||||
"Light in two media",
|
||||
"Snell's Law",
|
||||
"Multilayered glass",
|
||||
]
|
||||
]
|
||||
for mob in light_in_two, snells:
|
||||
mob.shift(-spacing)
|
||||
arrow1 = Arrow(brachy, optics)
|
||||
arrow2 = Arrow(optics, snells)
|
||||
point = Point(DOWN)
|
||||
|
||||
self.play(ShimmerIn(brachy))
|
||||
self.dither()
|
||||
self.play(
|
||||
ApplyMethod(brachy.shift, spacing),
|
||||
Transform(point, optics)
|
||||
)
|
||||
optics = point
|
||||
arrow1 = Arrow(optics, brachy)
|
||||
self.play(ShowCreation(arrow1))
|
||||
self.dither()
|
||||
arrow2 = Arrow(light_in_two, optics)
|
||||
self.play(
|
||||
ShowCreation(arrow2),
|
||||
ShimmerIn(light_in_two)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(light_in_two),
|
||||
GrowFromCenter(snells),
|
||||
DelayByOrder(
|
||||
ApplyMethod(arrow2.highlight, BLUE_D)
|
||||
)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeOut(optics),
|
||||
GrowFromCenter(multi),
|
||||
DelayByOrder(
|
||||
ApplyMethod(arrow1.highlight, BLUE_D)
|
||||
)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class BalanceCompetingFactors(Scene):
|
||||
args_list = [
|
||||
("Short", "Steep"),
|
||||
("Minimal time \\\\ in water", "Short path")
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def args_to_string(*words):
|
||||
return "".join([word.split(" ")[0] for word in words])
|
||||
|
||||
def construct(self, *words):
|
||||
factor1, factor2 = [
|
||||
TextMobject("Factor %d"%x).highlight(c)
|
||||
for x, c in [
|
||||
(1, RED_D),
|
||||
(2, BLUE_D)
|
||||
]
|
||||
]
|
||||
real_factor1, real_factor2 = map(TextMobject, words)
|
||||
for word in factor1, factor2, real_factor1, real_factor2:
|
||||
word.shift(0.2*UP-word.get_bottom())
|
||||
for f1 in factor1, real_factor1:
|
||||
f1.highlight(RED_D)
|
||||
f1.shift(2*LEFT)
|
||||
for f2 in factor2, real_factor2:
|
||||
f2.highlight(BLUE_D)
|
||||
f2.shift(2*RIGHT)
|
||||
line = Line(
|
||||
factor1.get_left(),
|
||||
factor2.get_right()
|
||||
)
|
||||
line.center()
|
||||
self.balancers = Mobject(factor1, factor2, line)
|
||||
self.hidden_balancers = Mobject(real_factor1, real_factor2)
|
||||
|
||||
triangle = Polygon(RIGHT, np.sqrt(3)*UP, LEFT)
|
||||
triangle.next_to(line, DOWN, buff = 0)
|
||||
|
||||
self.add(triangle, self.balancers)
|
||||
self.rotate(1)
|
||||
self.rotate(-2)
|
||||
self.dither()
|
||||
self.play(Transform(
|
||||
factor1, real_factor1,
|
||||
path_func = path_along_arc(np.pi/4)
|
||||
))
|
||||
self.rotate(2)
|
||||
self.dither()
|
||||
self.play(Transform(
|
||||
factor2, real_factor2,
|
||||
path_func = path_along_arc(np.pi/4)
|
||||
))
|
||||
self.rotate(-2)
|
||||
self.dither()
|
||||
self.rotate(1)
|
||||
|
||||
def rotate(self, factor):
|
||||
angle = np.pi/11
|
||||
self.play(Rotate(
|
||||
self.balancers,
|
||||
factor*angle,
|
||||
run_time = abs(factor)
|
||||
))
|
||||
self.hidden_balancers.rotate(factor*angle)
|
||||
|
||||
|
||||
|
||||
|
||||
class Challenge(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("""
|
||||
Can you find a new solution to the
|
||||
Brachistochrone problem by finding
|
||||
an intuitive reason that time-minimizing
|
||||
curves look like straight lines in
|
||||
$t$-$\\theta$ space?
|
||||
"""))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class Section1(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("Section 1: Johann Bernoulli's insight"))
|
||||
self.dither()
|
||||
|
||||
class Section2(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject(
|
||||
"Section 2: Mark Levi's insight, and a challenge",
|
||||
size = "\\large"
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class NarratorInterjection(Scene):
|
||||
def construct(self):
|
||||
words1 = TexMobject("<\\text{Narrator interjection}>")
|
||||
words2 = TexMobject("<\\!/\\text{Narrator interjection}>")
|
||||
self.add(words1)
|
||||
self.dither()
|
||||
self.clear()
|
||||
self.add(words2)
|
||||
self.dither()
|
||||
|
||||
|
||||
class ThisCouldBeTheEnd(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject([
|
||||
"This could be the end\\dots",
|
||||
"but\\dots"
|
||||
])
|
||||
for part in words.split():
|
||||
self.play(ShimmerIn(part))
|
||||
self.dither()
|
||||
|
||||
|
||||
class MyOwnChallenge(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("My own challenge:"))
|
||||
self.dither()
|
||||
|
||||
|
||||
class WarmupChallenge(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("\\large Warm-up challenge: Confirm this for yourself"))
|
||||
self.dither()
|
||||
|
||||
class FindAnotherSolution(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("Find another brachistochrone solution\\dots"))
|
||||
self.dither()
|
||||
|
||||
|
||||
class ProofOfSnellsLaw(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("Proof of Snell's law:"))
|
||||
self.dither()
|
||||
|
||||
|
||||
class CondensedVersion(Scene):
|
||||
def construct(self):
|
||||
snells = TextMobject("Snell's")
|
||||
snells.shift(-snells.get_left())
|
||||
snells.to_edge(UP)
|
||||
for vect in [RIGHT, RIGHT, LEFT, DOWN, DOWN, DOWN]:
|
||||
snells.add(snells.copy().next_to(snells, vect))
|
||||
snells.ingest_submobjects()
|
||||
snells.show()
|
||||
condensed = TextMobject("condensed")
|
||||
|
||||
self.add(snells)
|
||||
self.dither()
|
||||
self.play(DelayByOrder(
|
||||
Transform(snells, condensed, run_time = 2)
|
||||
))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
0
old_projects/hilbert/__init__.py
Normal file
0
old_projects/hilbert/__init__.py
Normal file
347
old_projects/hilbert/fractal_porn.py
Normal file
347
old_projects/hilbert/fractal_porn.py
Normal file
@ -0,0 +1,347 @@
|
||||
from mobject import Mobject, Point, Mobject1D
|
||||
from scene import Scene
|
||||
from animation.transform import \
|
||||
Transform, ShimmerIn, FadeIn, FadeOut, ApplyMethod
|
||||
from animation.simple_animations import \
|
||||
ShowCreation, DelayByOrder
|
||||
from topics.geometry import Line, Arc, Arrow
|
||||
from mobject.tex_mobject import TexMobject, TextMobject
|
||||
|
||||
from helpers import *
|
||||
from hilbert.curves import *
|
||||
|
||||
class Intro(TransformOverIncreasingOrders):
|
||||
@staticmethod
|
||||
def args_to_string(*args):
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def string_to_args(string):
|
||||
raise Exception("string_to_args Not Implemented!")
|
||||
|
||||
def construct(self):
|
||||
words1 = TextMobject(
|
||||
"If you watched my video about Hilbert's space-filling curve\\dots"
|
||||
)
|
||||
words2 = TextMobject(
|
||||
"\\dots you might be curious to see what a few other space-filling curves look like."
|
||||
)
|
||||
words2.scale(0.8)
|
||||
for words in words1, words2:
|
||||
words.to_edge(UP, buff = 0.2)
|
||||
|
||||
self.setup(HilbertCurve)
|
||||
self.play(ShimmerIn(words1))
|
||||
for x in range(4):
|
||||
self.increase_order()
|
||||
self.remove(words1)
|
||||
self.increase_order(
|
||||
ShimmerIn(words2)
|
||||
)
|
||||
for x in range(4):
|
||||
self.increase_order()
|
||||
|
||||
|
||||
|
||||
class BringInPeano(Intro):
|
||||
def construct(self):
|
||||
words1 = TextMobject("""
|
||||
For each one, see if you can figure out what
|
||||
the pattern of construction is.
|
||||
""")
|
||||
words2 = TextMobject("""
|
||||
This one is the Peano curve.
|
||||
""")
|
||||
words3 = TextMobject("""
|
||||
It is the original space-filling curve.
|
||||
""")
|
||||
self.setup(PeanoCurve)
|
||||
self.play(ShimmerIn(words1))
|
||||
self.dither(5)
|
||||
self.remove(words1)
|
||||
self.add(words2.to_edge(UP))
|
||||
for x in range(3):
|
||||
self.increase_order()
|
||||
self.remove(words2)
|
||||
self.increase_order(ShimmerIn(words3.to_edge(UP)))
|
||||
for x in range(2):
|
||||
self.increase_order()
|
||||
|
||||
|
||||
class FillOtherShapes(Intro):
|
||||
def construct(self):
|
||||
words1 = TextMobject("""
|
||||
But of course, there's no reason we should limit
|
||||
ourselves to filling in squares.
|
||||
""")
|
||||
words2 = TextMobject("""
|
||||
Here's a simple triangle-filling curve I defined
|
||||
in a style reflective of a Hilbert curve.
|
||||
""")
|
||||
words1.to_edge(UP)
|
||||
words2.scale(0.8).to_edge(UP, buff = 0.2)
|
||||
|
||||
self.setup(TriangleFillingCurve)
|
||||
self.play(ShimmerIn(words1))
|
||||
for x in range(3):
|
||||
self.increase_order()
|
||||
self.remove(words1)
|
||||
self.add(words2)
|
||||
for x in range(5):
|
||||
self.increase_order()
|
||||
|
||||
class SmallerFlowSnake(FlowSnake):
|
||||
CONFIG = {
|
||||
"radius" : 4
|
||||
}
|
||||
|
||||
class MostDelightfulName(Intro):
|
||||
def construct(self):
|
||||
words1 = TextMobject("""
|
||||
This one has the most delightful name,
|
||||
thanks to mathematician/programmer Bill Gosper:
|
||||
""")
|
||||
words2 = TextMobject("``Flow Snake''")
|
||||
words3 = TextMobject("""
|
||||
What makes this one particularly interesting
|
||||
is that the boundary itself is a fractal.
|
||||
""")
|
||||
for words in words1, words2, words3:
|
||||
words.to_edge(UP)
|
||||
|
||||
self.setup(SmallerFlowSnake)
|
||||
self.play(ShimmerIn(words1))
|
||||
for x in range(3):
|
||||
self.increase_order()
|
||||
self.remove(words1)
|
||||
self.add(words2)
|
||||
for x in range(3):
|
||||
self.increase_order()
|
||||
self.remove(words2)
|
||||
self.play(ShimmerIn(words3))
|
||||
|
||||
|
||||
|
||||
class SurpriseFractal(Intro):
|
||||
def construct(self):
|
||||
words = TextMobject("""
|
||||
It might come as a surprise how some well-known
|
||||
fractals can be described with curves.
|
||||
""")
|
||||
words.to_edge(UP)
|
||||
|
||||
self.setup(Sierpinski)
|
||||
self.add(TextMobject("Speaking of other fractals\\dots"))
|
||||
self.dither(3)
|
||||
self.clear()
|
||||
self.play(ShimmerIn(words))
|
||||
for x in range(9):
|
||||
self.increase_order()
|
||||
|
||||
|
||||
class IntroduceKoch(Intro):
|
||||
def construct(self):
|
||||
words = map(TextMobject, [
|
||||
"This is another famous fractal.",
|
||||
"The ``Koch Snowflake''",
|
||||
"Let's finish things off by seeing how to turn \
|
||||
this into a space-filling curve"
|
||||
])
|
||||
for text in words:
|
||||
text.to_edge(UP)
|
||||
|
||||
self.setup(KochCurve)
|
||||
self.add(words[0])
|
||||
for x in range(3):
|
||||
self.increase_order()
|
||||
self.remove(words[0])
|
||||
self.add(words[1])
|
||||
for x in range(4):
|
||||
self.increase_order()
|
||||
self.remove(words[1])
|
||||
self.add(words[2])
|
||||
self.dither(6)
|
||||
|
||||
class StraightKoch(KochCurve):
|
||||
CONFIG = {
|
||||
"axiom" : "A"
|
||||
}
|
||||
|
||||
class SharperKoch(StraightKoch):
|
||||
CONFIG = {
|
||||
"angle" : 0.9*np.pi/2,
|
||||
}
|
||||
|
||||
class DullerKoch(StraightKoch):
|
||||
CONFIG = {
|
||||
"angle" : np.pi/6,
|
||||
}
|
||||
|
||||
class SpaceFillingKoch(StraightKoch):
|
||||
CONFIG = {
|
||||
"angle" : np.pi/2,
|
||||
}
|
||||
|
||||
|
||||
|
||||
class FromKochToSpaceFilling(Scene):
|
||||
def construct(self):
|
||||
self.max_order = 7
|
||||
|
||||
self.revisit_koch()
|
||||
self.show_angles()
|
||||
self.show_change_side_by_side()
|
||||
|
||||
|
||||
def revisit_koch(self):
|
||||
words = map(TextMobject, [
|
||||
"First, look at how one section of this curve is made.",
|
||||
"This pattern of four lines is the ``seed''",
|
||||
"With each iteration, every straight line is \
|
||||
replaced with an appropriately small copy of the seed",
|
||||
])
|
||||
for text in words:
|
||||
text.to_edge(UP)
|
||||
|
||||
self.add(words[0])
|
||||
curve = StraightKoch(order = self.max_order)
|
||||
self.play(Transform(
|
||||
curve,
|
||||
StraightKoch(order = 1),
|
||||
run_time = 5
|
||||
))
|
||||
self.remove(words[0])
|
||||
self.add(words[1])
|
||||
self.dither(4)
|
||||
self.remove(words[1])
|
||||
self.add(words[2])
|
||||
self.dither(3)
|
||||
for order in range(2, self.max_order):
|
||||
self.play(Transform(
|
||||
curve,
|
||||
StraightKoch(order = order)
|
||||
))
|
||||
if order == 2:
|
||||
self.dither(2)
|
||||
elif order == 3:
|
||||
self.dither()
|
||||
self.clear()
|
||||
|
||||
|
||||
|
||||
def show_angles(self):
|
||||
words = TextMobject("""
|
||||
Let's see what happens as we change
|
||||
the angle in this seed
|
||||
""")
|
||||
words.to_edge(UP)
|
||||
koch, sharper_koch, duller_koch = curves = [
|
||||
CurveClass(order = 1)
|
||||
for CurveClass in StraightKoch, SharperKoch, DullerKoch
|
||||
]
|
||||
arcs = [
|
||||
Arc(
|
||||
2*(np.pi/2 - curve.angle),
|
||||
radius = r,
|
||||
start_angle = np.pi+curve.angle
|
||||
).shift(curve.points[curve.get_num_points()/2])
|
||||
for curve, r in zip(curves, [0.6, 0.7, 0.4])
|
||||
]
|
||||
theta = TexMobject("\\theta")
|
||||
theta.shift(arcs[0].get_center()+2.5*DOWN)
|
||||
arrow = Arrow(theta, arcs[0])
|
||||
|
||||
self.add(words, koch)
|
||||
self.play(ShowCreation(arcs[0]))
|
||||
self.play(
|
||||
ShowCreation(arrow),
|
||||
ShimmerIn(theta)
|
||||
)
|
||||
self.dither(2)
|
||||
self.remove(theta, arrow)
|
||||
self.play(
|
||||
Transform(koch, duller_koch),
|
||||
Transform(arcs[0], arcs[2]),
|
||||
)
|
||||
self.play(
|
||||
Transform(koch, sharper_koch),
|
||||
Transform(arcs[0], arcs[1]),
|
||||
)
|
||||
self.clear()
|
||||
|
||||
def show_change_side_by_side(self):
|
||||
|
||||
seed = TextMobject("Seed")
|
||||
seed.shift(3*LEFT+2*DOWN)
|
||||
fractal = TextMobject("Fractal")
|
||||
fractal.shift(3*RIGHT+2*DOWN)
|
||||
words = map(TextMobject, [
|
||||
"A sharper angle results in a richer curve",
|
||||
"A more obtuse angle gives a sparser curve",
|
||||
"And as the angle approaches 0\\dots",
|
||||
"We have a new space-filling curve."
|
||||
])
|
||||
for text in words:
|
||||
text.to_edge(UP)
|
||||
sharper, duller, space_filling = [
|
||||
CurveClass(order = 1).shift(3*LEFT)
|
||||
for CurveClass in SharperKoch, DullerKoch, SpaceFillingKoch
|
||||
]
|
||||
shaper_f, duller_f, space_filling_f = [
|
||||
CurveClass(order = self.max_order).shift(3*RIGHT)
|
||||
for CurveClass in SharperKoch, DullerKoch, SpaceFillingKoch
|
||||
]
|
||||
|
||||
self.add(words[0])
|
||||
left_curve = SharperKoch(order = 1)
|
||||
right_curve = SharperKoch(order = 1)
|
||||
self.play(
|
||||
Transform(left_curve, sharper),
|
||||
ApplyMethod(right_curve.shift, 3*RIGHT),
|
||||
)
|
||||
self.play(
|
||||
Transform(
|
||||
right_curve,
|
||||
SharperKoch(order = 2).shift(3*RIGHT)
|
||||
),
|
||||
ShimmerIn(seed),
|
||||
ShimmerIn(fractal)
|
||||
)
|
||||
for order in range(3, self.max_order):
|
||||
self.play(Transform(
|
||||
right_curve,
|
||||
SharperKoch(order = order).shift(3*RIGHT)
|
||||
))
|
||||
self.remove(words[0])
|
||||
self.add(words[1])
|
||||
kwargs = {
|
||||
"run_time" : 4,
|
||||
}
|
||||
self.play(
|
||||
Transform(left_curve, duller, **kwargs),
|
||||
Transform(right_curve, duller_f, **kwargs)
|
||||
)
|
||||
self.dither()
|
||||
kwargs["run_time"] = 7
|
||||
kwargs["rate_func"] = None
|
||||
self.remove(words[1])
|
||||
self.add(words[2])
|
||||
self.play(
|
||||
Transform(left_curve, space_filling, **kwargs),
|
||||
Transform(right_curve, space_filling_f, **kwargs)
|
||||
)
|
||||
self.remove(words[2])
|
||||
self.add(words[3])
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1089
old_projects/hilbert/section1.py
Normal file
1089
old_projects/hilbert/section1.py
Normal file
File diff suppressed because it is too large
Load Diff
1095
old_projects/hilbert/section2.py
Normal file
1095
old_projects/hilbert/section2.py
Normal file
File diff suppressed because it is too large
Load Diff
310
old_projects/hilbert/section3.py
Normal file
310
old_projects/hilbert/section3.py
Normal file
@ -0,0 +1,310 @@
|
||||
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,\
|
||||
TransformAnimations, ShimmerIn, FadeOut, FadeIn
|
||||
from animation.simple_animations import \
|
||||
ShowCreation, DelayByOrder
|
||||
from animation.playground import Vibrate
|
||||
|
||||
from topics.geometry import \
|
||||
Line, Dot, Arrow, Grid, Square, Point, Polygon
|
||||
from topics.characters import \
|
||||
ThoughtBubble, SpeechBubble, Mathematician, Mortimer
|
||||
from topics.number_line import UnitInterval
|
||||
from topics.three_dimensions import Stars
|
||||
|
||||
from mobject.region import region_from_polygon_vertices
|
||||
|
||||
import displayer as disp
|
||||
|
||||
from hilbert.curves import \
|
||||
TransformOverIncreasingOrders, FlowSnake, HilbertCurve, \
|
||||
SnakeCurve, Sierpinski
|
||||
from hilbert.section1 import get_mathy_and_bubble
|
||||
|
||||
|
||||
from helpers import *
|
||||
|
||||
|
||||
|
||||
class SectionThree(Scene):
|
||||
def construct(self):
|
||||
self.add(TextMobject("A few words on the usefulness of infinite math"))
|
||||
self.dither()
|
||||
|
||||
|
||||
class InfiniteResultsFiniteWorld(Scene):
|
||||
def construct(self):
|
||||
left_words = TextMobject("Infinite result")
|
||||
right_words = TextMobject("Finite world")
|
||||
for words in left_words, right_words:
|
||||
words.scale(0.8)
|
||||
left_formula = TexMobject(
|
||||
"\\sum_{n = 0}^{\\infty} 2^n = -1"
|
||||
)
|
||||
right_formula = TexMobject("111\\cdots111")
|
||||
for formula in left_formula, right_formula:
|
||||
formula.add(
|
||||
Brace(formula, UP),
|
||||
)
|
||||
formula.ingest_submobjects()
|
||||
right_overwords = TextMobject(
|
||||
"\\substack{\
|
||||
\\text{How computers} \\\\ \
|
||||
\\text{represent $-1$}\
|
||||
}"
|
||||
).scale(1.5)
|
||||
|
||||
left_mobs = [left_words, left_formula]
|
||||
right_mobs = [right_words, right_formula]
|
||||
for mob in left_mobs:
|
||||
mob.to_edge(RIGHT, buff = 1)
|
||||
mob.shift(SPACE_WIDTH*LEFT)
|
||||
for mob in right_mobs:
|
||||
mob.to_edge(LEFT, buff = 1)
|
||||
mob.shift(SPACE_WIDTH*RIGHT)
|
||||
arrow = Arrow(left_words, right_words)
|
||||
right_overwords.next_to(right_formula, UP)
|
||||
|
||||
self.play(ShimmerIn(left_words))
|
||||
self.play(ShowCreation(arrow))
|
||||
self.play(ShimmerIn(right_words))
|
||||
self.dither()
|
||||
self.play(
|
||||
ShimmerIn(left_formula),
|
||||
ApplyMethod(left_words.next_to, left_formula, UP)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
ShimmerIn(right_formula),
|
||||
Transform(right_words, right_overwords)
|
||||
)
|
||||
self.dither()
|
||||
self.finite_analog(
|
||||
Mobject(left_formula, left_words),
|
||||
arrow,
|
||||
Mobject(right_formula, right_words)
|
||||
)
|
||||
|
||||
|
||||
def finite_analog(self, left_mob, arrow, right_mob):
|
||||
self.clear()
|
||||
self.add(left_mob, arrow, right_mob)
|
||||
ex = TextMobject("\\times")
|
||||
ex.highlight(RED)
|
||||
# ex.shift(arrow.get_center())
|
||||
middle = TexMobject(
|
||||
"\\sum_{n=0}^N 2^n \\equiv -1 \\mod 2^{N+1}"
|
||||
)
|
||||
finite_analog = TextMobject("Finite analog")
|
||||
finite_analog.scale(0.8)
|
||||
brace = Brace(middle, UP)
|
||||
finite_analog.next_to(brace, UP)
|
||||
new_left = left_mob.copy().to_edge(LEFT)
|
||||
new_right = right_mob.copy().to_edge(RIGHT)
|
||||
left_arrow, right_arrow = [
|
||||
Arrow(
|
||||
mob1.get_right()[0]*RIGHT,
|
||||
mob2.get_left()[0]*RIGHT,
|
||||
buff = 0
|
||||
)
|
||||
for mob1, mob2 in [
|
||||
(new_left, middle),
|
||||
(middle, new_right)
|
||||
]
|
||||
]
|
||||
for mob in ex, middle:
|
||||
mob.sort_points(np.linalg.norm)
|
||||
|
||||
self.play(GrowFromCenter(ex))
|
||||
self.dither()
|
||||
self.play(
|
||||
Transform(left_mob, new_left),
|
||||
Transform(arrow.copy(), left_arrow),
|
||||
DelayByOrder(Transform(ex, middle)),
|
||||
Transform(arrow, right_arrow),
|
||||
Transform(right_mob, new_right)
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
ShimmerIn(finite_analog)
|
||||
)
|
||||
self.dither()
|
||||
self.equivalence(
|
||||
left_mob,
|
||||
left_arrow,
|
||||
Mobject(middle, brace, finite_analog)
|
||||
)
|
||||
|
||||
def equivalence(self, left_mob, arrow, right_mob):
|
||||
self.clear()
|
||||
self.add(left_mob, arrow, right_mob)
|
||||
words = TextMobject("is equivalent to")
|
||||
words.shift(0.25*LEFT)
|
||||
words.highlight(BLUE)
|
||||
new_left = left_mob.copy().shift(RIGHT)
|
||||
new_right = right_mob.copy()
|
||||
new_right.shift(
|
||||
(words.get_right()[0]-\
|
||||
right_mob.get_left()[0]+\
|
||||
0.5
|
||||
)*RIGHT
|
||||
)
|
||||
for mob in arrow, words:
|
||||
mob.sort_points(np.linalg.norm)
|
||||
|
||||
self.play(
|
||||
ApplyMethod(left_mob.shift, RIGHT),
|
||||
Transform(arrow, words),
|
||||
ApplyMethod(right_mob.to_edge, RIGHT)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
class HilbertCurvesStayStable(Scene):
|
||||
def construct(self):
|
||||
scale_factor = 0.9
|
||||
grid = Grid(4, 4, stroke_width = 1)
|
||||
curve = HilbertCurve(order = 2)
|
||||
for mob in grid, curve:
|
||||
mob.scale(scale_factor)
|
||||
words = TextMobject("""
|
||||
Sequence of curves is stable
|
||||
$\\leftrightarrow$ existence of limit curve
|
||||
""", size = "\\normal")
|
||||
words.scale(1.25)
|
||||
words.to_edge(UP)
|
||||
|
||||
self.add(curve, grid)
|
||||
self.dither()
|
||||
for n in range(3, 7):
|
||||
if n == 5:
|
||||
self.play(ShimmerIn(words))
|
||||
new_grid = Grid(2**n, 2**n, stroke_width = 1)
|
||||
new_curve = HilbertCurve(order = n)
|
||||
for mob in new_grid, new_curve:
|
||||
mob.scale(scale_factor)
|
||||
self.play(
|
||||
ShowCreation(new_grid),
|
||||
Animation(curve)
|
||||
)
|
||||
self.remove(grid)
|
||||
grid = new_grid
|
||||
self.play(Transform(curve, new_curve))
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
class InfiniteObjectsEncapsulateFiniteObjects(Scene):
|
||||
def get_triangles(self):
|
||||
triangle = Polygon(
|
||||
LEFT/np.sqrt(3),
|
||||
UP,
|
||||
RIGHT/np.sqrt(3),
|
||||
color = GREEN
|
||||
)
|
||||
triangles = Mobject(
|
||||
triangle.copy().scale(0.5).shift(LEFT),
|
||||
triangle,
|
||||
triangle.copy().scale(0.3).shift(0.5*UP+RIGHT)
|
||||
)
|
||||
triangles.center()
|
||||
return triangles
|
||||
|
||||
def construct(self):
|
||||
words =[
|
||||
TextMobject(text, size = "\\large")
|
||||
for text in [
|
||||
"Truths about infinite objects",
|
||||
" encapsulate ",
|
||||
"facts about finite objects"
|
||||
]
|
||||
]
|
||||
|
||||
words[0].highlight(RED)
|
||||
words[1].next_to(words[0])
|
||||
words[2].highlight(GREEN).next_to(words[1])
|
||||
Mobject(*words).center().to_edge(UP)
|
||||
infinite_objects = [
|
||||
TexMobject(
|
||||
"\\sum_{n=0}^\\infty",
|
||||
size = "\\normal"
|
||||
).highlight(RED_E),
|
||||
Sierpinski(order = 8).scale(0.3),
|
||||
TextMobject(
|
||||
"$\\exists$ something infinite $\\dots$"
|
||||
).highlight(RED_B)
|
||||
]
|
||||
finite_objects = [
|
||||
TexMobject(
|
||||
"\\sum_{n=0}^N",
|
||||
size = "\\normal"
|
||||
).highlight(GREEN_E),
|
||||
self.get_triangles(),
|
||||
TextMobject(
|
||||
"$\\forall$ finite somethings $\\dots$"
|
||||
).highlight(GREEN_B)
|
||||
]
|
||||
for infinite, finite, n in zip(infinite_objects, finite_objects, it.count(1, 2)):
|
||||
infinite.next_to(words[0], DOWN, buff = n)
|
||||
finite.next_to(words[2], DOWN, buff = n)
|
||||
|
||||
self.play(ShimmerIn(words[0]))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(infinite_objects[0]))
|
||||
self.play(ShowCreation(infinite_objects[1]))
|
||||
self.play(ShimmerIn(infinite_objects[2]))
|
||||
self.dither()
|
||||
self.play(ShimmerIn(words[1]), ShimmerIn(words[2]))
|
||||
self.play(ShimmerIn(finite_objects[0]))
|
||||
self.play(ShowCreation(finite_objects[1]))
|
||||
self.play(ShimmerIn(finite_objects[2]))
|
||||
self.dither()
|
||||
|
||||
|
||||
class StatementRemovedFromReality(Scene):
|
||||
def construct(self):
|
||||
mathy, bubble = get_mathy_and_bubble()
|
||||
bubble.stretch_to_fit_width(4)
|
||||
mathy.to_corner(DOWN+LEFT)
|
||||
bubble.pin_to(mathy)
|
||||
bubble.shift(LEFT)
|
||||
morty = Mortimer()
|
||||
morty.to_corner(DOWN+RIGHT)
|
||||
morty_bubble = SpeechBubble()
|
||||
morty_bubble.stretch_to_fit_width(4)
|
||||
morty_bubble.pin_to(morty)
|
||||
bubble.write("""
|
||||
Did you know a curve \\\\
|
||||
can fill all space?
|
||||
""")
|
||||
morty_bubble.write("Who cares?")
|
||||
|
||||
self.add(mathy, morty)
|
||||
for bub, buddy in [(bubble, mathy), (morty_bubble, morty)]:
|
||||
self.play(Transform(
|
||||
Point(bub.get_tip()),
|
||||
bub
|
||||
))
|
||||
self.play(ShimmerIn(bub.content))
|
||||
self.play(ApplyMethod(
|
||||
buddy.blink,
|
||||
rate_func = squish_rate_func(there_and_back)
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
83
old_projects/number_line_scene.py
Normal file
83
old_projects/number_line_scene.py
Normal file
@ -0,0 +1,83 @@
|
||||
class NumberLineScene(Scene):
|
||||
def construct(self, **number_line_config):
|
||||
self.number_line = NumberLine(**number_line_config)
|
||||
self.displayed_numbers = self.number_line.default_numbers_to_display()
|
||||
self.number_mobs = self.number_line.get_number_mobjects(*self.displayed_numbers)
|
||||
self.add(self.number_line, *self.number_mobs)
|
||||
|
||||
def zoom_in_on(self, number, zoom_factor, run_time = 2.0):
|
||||
unit_length_to_spatial_width = self.number_line.unit_length_to_spatial_width*zoom_factor
|
||||
radius = SPACE_WIDTH/unit_length_to_spatial_width
|
||||
tick_frequency = 10**(np.floor(np.log10(radius)))
|
||||
left_tick = tick_frequency*(np.ceil((number-radius)/tick_frequency))
|
||||
new_number_line = NumberLine(
|
||||
numerical_radius = radius,
|
||||
unit_length_to_spatial_width = unit_length_to_spatial_width,
|
||||
tick_frequency = tick_frequency,
|
||||
leftmost_tick = left_tick,
|
||||
number_at_center = number
|
||||
)
|
||||
new_displayed_numbers = new_number_line.default_numbers_to_display()
|
||||
new_number_mobs = new_number_line.get_number_mobjects(*new_displayed_numbers)
|
||||
|
||||
transforms = []
|
||||
additional_mobjects = []
|
||||
squished_new_line = new_number_line.copy()
|
||||
squished_new_line.scale(1.0/zoom_factor)
|
||||
squished_new_line.shift(self.number_line.number_to_point(number))
|
||||
squished_new_line.points[:,1] = self.number_line.number_to_point(0)[1]
|
||||
transforms.append(Transform(squished_new_line, new_number_line))
|
||||
for mob, num in zip(new_number_mobs, new_displayed_numbers):
|
||||
point = Point(self.number_line.number_to_point(num))
|
||||
point.shift(new_number_line.get_vertical_number_offset())
|
||||
transforms.append(Transform(point, mob))
|
||||
for mob in self.mobjects:
|
||||
if mob == self.number_line:
|
||||
new_mob = mob.copy()
|
||||
new_mob.shift(-self.number_line.number_to_point(number))
|
||||
new_mob.stretch(zoom_factor, 0)
|
||||
transforms.append(Transform(mob, new_mob))
|
||||
continue
|
||||
mob_center = mob.get_center()
|
||||
number_under_center = self.number_line.point_to_number(mob_center)
|
||||
new_point = new_number_line.number_to_point(number_under_center)
|
||||
new_point += mob_center[1]*UP
|
||||
if mob in self.number_mobs:
|
||||
transforms.append(Transform(mob, Point(new_point)))
|
||||
else:
|
||||
transforms.append(ApplyMethod(mob.shift, new_point - mob_center))
|
||||
additional_mobjects.append(mob)
|
||||
line_to_hide_pixelation = Line(
|
||||
self.number_line.get_left(),
|
||||
self.number_line.get_right(),
|
||||
color = self.number_line.get_color()
|
||||
)
|
||||
self.add(line_to_hide_pixelation)
|
||||
self.play(*transforms, run_time = run_time)
|
||||
self.clear()
|
||||
self.number_line = new_number_line
|
||||
self.displayed_numbers = new_displayed_numbers
|
||||
self.number_mobs = new_number_mobs
|
||||
self.add(self.number_line, *self.number_mobs)
|
||||
self.add(*additional_mobjects)
|
||||
|
||||
def show_multiplication(self, num, **kwargs):
|
||||
if "path_func" not in kwargs:
|
||||
if num > 0:
|
||||
kwargs["path_func"] = straight_path
|
||||
else:
|
||||
kwargs["path_func"] = counterclockwise_path()
|
||||
self.play(*[
|
||||
ApplyMethod(self.number_line.stretch, num, 0, **kwargs)
|
||||
]+[
|
||||
ApplyMethod(mob.shift, (num-1)*mob.get_center()[0]*RIGHT, **kwargs)
|
||||
for mob in self.number_mobs
|
||||
])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
0
old_projects/triangle_of_power/__init__.py
Normal file
0
old_projects/triangle_of_power/__init__.py
Normal file
Reference in New Issue
Block a user