mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Messy pile of brachistochrone work
This commit is contained in:
parent
9262c395ce
commit
19e3c7f849
6 changed files with 627 additions and 424 deletions
|
@ -153,29 +153,6 @@ class PathSlidingScene(Scene):
|
||||||
"gravity" : 3,
|
"gravity" : 3,
|
||||||
"delta_t" : 0.05
|
"delta_t" : 0.05
|
||||||
}
|
}
|
||||||
def get_time_slices(self, points):
|
|
||||||
dt_list = np.zeros(len(points))
|
|
||||||
ds_list = np.apply_along_axis(
|
|
||||||
np.linalg.norm,
|
|
||||||
1,
|
|
||||||
points[1:]-points[:-1]
|
|
||||||
)
|
|
||||||
delta_y_list = np.abs(points[0, 1] - 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])
|
|
||||||
return mobject
|
|
||||||
|
|
||||||
def slide(self, mobject, path, roll = False):
|
def slide(self, mobject, path, roll = False):
|
||||||
points = path.points
|
points = path.points
|
||||||
time_slices = self.get_time_slices(points)
|
time_slices = self.get_time_slices(points)
|
||||||
|
@ -208,6 +185,29 @@ class PathSlidingScene(Scene):
|
||||||
self.add(self.slider)
|
self.add(self.slider)
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
def get_time_slices(self, points):
|
||||||
|
dt_list = np.zeros(len(points))
|
||||||
|
ds_list = np.apply_along_axis(
|
||||||
|
np.linalg.norm,
|
||||||
|
1,
|
||||||
|
points[1:]-points[:-1]
|
||||||
|
)
|
||||||
|
delta_y_list = np.abs(points[0, 1] - 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])
|
||||||
|
return mobject
|
||||||
|
|
||||||
def write_time(self, time):
|
def write_time(self, time):
|
||||||
if hasattr(self, "time_mob"):
|
if hasattr(self, "time_mob"):
|
||||||
self.remove(self.time_mob)
|
self.remove(self.time_mob)
|
||||||
|
@ -223,6 +223,16 @@ class PathSlidingScene(Scene):
|
||||||
theta = arc_length / radius
|
theta = arc_length / radius
|
||||||
mobject.rotate_in_place(-theta)
|
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):
|
class TryManyPaths(PathSlidingScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
randy = Randolph()
|
randy = Randolph()
|
||||||
|
@ -271,17 +281,17 @@ class TryManyPaths(PathSlidingScene):
|
||||||
Line(DOWN, DOWN+3*RIGHT)
|
Line(DOWN, DOWN+3*RIGHT)
|
||||||
).ingest_sub_mobjects().highlight(GREEN)
|
).ingest_sub_mobjects().highlight(GREEN)
|
||||||
paths = [
|
paths = [
|
||||||
Line(7*LEFT, 7*RIGHT, color = RED_D),
|
|
||||||
LoopTheLoop(),
|
|
||||||
FunctionGraph(
|
|
||||||
lambda x : 0.05*(x**2)+0.1*np.sin(2*x)
|
|
||||||
),
|
|
||||||
Arc(
|
Arc(
|
||||||
angle = np.pi/2,
|
angle = np.pi/2,
|
||||||
radius = 3,
|
radius = 3,
|
||||||
start_angle = 4
|
start_angle = 4
|
||||||
),
|
),
|
||||||
|
LoopTheLoop(),
|
||||||
|
Line(7*LEFT, 7*RIGHT, color = RED_D),
|
||||||
sharp_corner,
|
sharp_corner,
|
||||||
|
FunctionGraph(
|
||||||
|
lambda x : 0.05*(x**2)+0.1*np.sin(2*x)
|
||||||
|
),
|
||||||
FunctionGraph(
|
FunctionGraph(
|
||||||
lambda x : x**2,
|
lambda x : x**2,
|
||||||
x_min = -3,
|
x_min = -3,
|
||||||
|
@ -294,40 +304,60 @@ class TryManyPaths(PathSlidingScene):
|
||||||
return paths + [cycloid]
|
return paths + [cycloid]
|
||||||
|
|
||||||
def align_paths(self, paths, target_path):
|
def align_paths(self, paths, target_path):
|
||||||
def path_displacement(path):
|
start = target_path.points[0]
|
||||||
return path.points[-1]-path.points[0]
|
end = target_path.point[-1]
|
||||||
target = path_displacement(target_path)
|
|
||||||
for path in paths:
|
for path in paths:
|
||||||
vect = path_displacement(path)
|
path.position_endpoints_on(start, end)
|
||||||
path.scale(np.linalg.norm(target)/np.linalg.norm(vect))
|
|
||||||
path.rotate(
|
|
||||||
angle_of_vector(target) - \
|
|
||||||
angle_of_vector(vect)
|
|
||||||
)
|
|
||||||
path.shift(target_path.points[0]-path.points[0])
|
|
||||||
|
|
||||||
|
|
||||||
class RollingRandolph(PathSlidingScene):
|
class RollingRandolph(PathSlidingScene):
|
||||||
def construct(self):
|
def construct(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)
|
|
||||||
randy = Randolph()
|
randy = Randolph()
|
||||||
randy.scale(RANDY_SCALE_VAL)
|
randy.scale(RANDY_SCALE_VAL)
|
||||||
randy.shift(-randy.get_bottom())
|
randy.shift(-randy.get_bottom())
|
||||||
|
self.add_cycloid_end_points()
|
||||||
|
self.slide(randy, self.cycloid, roll = True)
|
||||||
|
|
||||||
self.add(point_a, point_b, A, B, cycloid)
|
|
||||||
self.slide(randy, 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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
import operator as op
|
import operator as op
|
||||||
|
@ -17,16 +15,17 @@ from mobject.tex_mobject import TexMobject
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
|
from mobject.tex_mobject import TextMobject, TexMobject
|
||||||
|
|
||||||
from animation.transform import \
|
from animation.transform import \
|
||||||
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
Transform, CounterclockwiseTransform, ApplyPointwiseFunction,\
|
||||||
FadeIn, FadeOut, GrowFromCenter
|
FadeIn, FadeOut, GrowFromCenter, ShimmerIn, ApplyMethod
|
||||||
from animation.simple_animations import \
|
from animation.simple_animations import \
|
||||||
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder
|
ShowCreation, Homotopy, PhaseFlow, ApplyToCenters, DelayByOrder
|
||||||
from animation.playground import TurnInsideOut, Vibrate
|
from animation.playground import TurnInsideOut, Vibrate
|
||||||
from topics.geometry import \
|
from topics.geometry import \
|
||||||
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point
|
Line, Circle, Square, Grid, Rectangle, Arrow, Dot, Point
|
||||||
from topics.characters import Randolph, Mathematician
|
from topics.characters import Randolph, Mathematician, ThoughtBubble
|
||||||
from topics.functions import ParametricFunction
|
from topics.functions import ParametricFunction
|
||||||
from topics.number_line import NumberPlane
|
from topics.number_line import NumberPlane
|
||||||
from region import Region, region_from_polygon_vertices
|
from region import Region, region_from_polygon_vertices
|
||||||
|
@ -134,7 +133,7 @@ class TracePicture(Scene):
|
||||||
("Steven_Strogatz",),
|
("Steven_Strogatz",),
|
||||||
("Pierre_de_Fermat",),
|
("Pierre_de_Fermat",),
|
||||||
("Galileo_Galilei",),
|
("Galileo_Galilei",),
|
||||||
("Jakob_Bernoulli",),
|
("Jacob_Bernoulli",),
|
||||||
("Johann_Bernoulli2",),
|
("Johann_Bernoulli2",),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -191,16 +190,110 @@ class TracePicture(Scene):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class JohannThinksHeIsBetter(Scene):
|
||||||
|
def construct(self):
|
||||||
|
names = [
|
||||||
|
"Johann_Bernoulli2",
|
||||||
|
"Jacob_Bernoulli",
|
||||||
|
"Gottfried_Wilhelm_von_Leibniz",
|
||||||
|
]
|
||||||
|
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_sub_mobjects()
|
||||||
|
bubble.pin_to(pensive_johann)
|
||||||
|
bubble.shift(DOWN)
|
||||||
|
point = Point(johann.get_corner(UP+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, guy)),
|
||||||
|
ShimmerIn(guy.name_mob)
|
||||||
|
)
|
||||||
|
self.dither(3)
|
||||||
|
self.remove(guy.name_mob)
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ import itertools as it
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
from mobject.tex_mobject import TexMobject, TextMobject, Brace
|
||||||
from mobject import Mobject
|
from mobject import Mobject, Mobject1D
|
||||||
from mobject.image_mobject import \
|
from mobject.image_mobject import \
|
||||||
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
MobjectFromRegion, ImageMobject, MobjectFromPixelArray
|
||||||
from topics.three_dimensions import Stars
|
from topics.three_dimensions import Stars
|
||||||
|
@ -237,7 +237,7 @@ class ShowMultiplePathsThroughLens(ShowMultiplePathsScene):
|
||||||
self.end_point = 2*RIGHT
|
self.end_point = 2*RIGHT
|
||||||
|
|
||||||
def get_paths(self):
|
def get_paths(self):
|
||||||
alphas = [0.2, 0.45, 0.55, 0.8]
|
alphas = [0.25, 0.4, 0.58, 0.75]
|
||||||
lower_right, upper_right, upper_left, lower_left = map(
|
lower_right, upper_right, upper_left, lower_left = map(
|
||||||
self.lens.point_from_proportion, alphas
|
self.lens.point_from_proportion, alphas
|
||||||
)
|
)
|
||||||
|
@ -306,6 +306,219 @@ class ShowMultiplePathsInGlass(ShowMultiplePathsScene):
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class MultilayeredGlass(PhotonScene):
|
||||||
|
DEFAULT_CONFIG = {
|
||||||
|
"num_discrete_layers" : 5,
|
||||||
|
"num_variables" : 3,
|
||||||
|
"top_color" : BLUE_E,
|
||||||
|
"bottom_color" : BLUE_A,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.cycloid = Cycloid(end_theta = np.pi)
|
||||||
|
self.top = self.cycloid.get_top()[1]
|
||||||
|
self.bottom = self.cycloid.get_bottom()[1]-1
|
||||||
|
self.generate_layer_regions()
|
||||||
|
self.generate_discrete_path()
|
||||||
|
photon_run = self.photon_run_along_path(
|
||||||
|
self.augmented_path,
|
||||||
|
run_time = 1,
|
||||||
|
rate_func = rush_into
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.continuous_to_smooth()
|
||||||
|
self.paint_layers()
|
||||||
|
self.show_layer_variables()
|
||||||
|
self.play(photon_run)
|
||||||
|
self.play(ShowCreation(self.discrete_path))
|
||||||
|
self.isolate_bend_points()
|
||||||
|
# self.dither()
|
||||||
|
|
||||||
|
def continuous_to_smooth(self):
|
||||||
|
continuous = self.get_continuous_background()
|
||||||
|
layers = Mobject(*[
|
||||||
|
MobjectFromRegion(region, color)
|
||||||
|
for region, color in zip(
|
||||||
|
self.layer_regions, self.layer_colors
|
||||||
|
)
|
||||||
|
])
|
||||||
|
layers.ingest_sub_mobjects()
|
||||||
|
|
||||||
|
self.play(FadeIn(continuous))
|
||||||
|
self.play(Transform(continuous, layers))
|
||||||
|
self.remove(continuous)
|
||||||
|
self.paint_layers()
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def paint_layers(self):
|
||||||
|
# for region, color in zip(self.layer_regions, self.layer_colors):
|
||||||
|
# self.highlight_region(region, color)
|
||||||
|
for top, color in zip(self.layer_tops, self.layer_colors):
|
||||||
|
self.add(Line(
|
||||||
|
SPACE_WIDTH*LEFT+top*UP, SPACE_WIDTH*RIGHT+top*UP,
|
||||||
|
color = color
|
||||||
|
))
|
||||||
|
|
||||||
|
def get_continuous_background(self):
|
||||||
|
glass = MobjectFromRegion(Region(
|
||||||
|
lambda x, y : (y < self.top) & (y > self.bottom)
|
||||||
|
))
|
||||||
|
glass.gradient_highlight(self.top_color, self.bottom_color)
|
||||||
|
glass.scale_in_place(0.99)
|
||||||
|
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_layer_regions(self):
|
||||||
|
self.generate_layer_info()
|
||||||
|
self.layer_regions = [
|
||||||
|
Region(lambda x, y : (y < top) & (y > top-self.layer_thickness))
|
||||||
|
for top in self.layer_tops
|
||||||
|
]
|
||||||
|
|
||||||
|
def generate_discrete_path(self):
|
||||||
|
points = self.cycloid.points
|
||||||
|
indices = [
|
||||||
|
np.argmin(np.abs(points[:, 1]-top))
|
||||||
|
for top in self.layer_tops
|
||||||
|
]
|
||||||
|
self.bend_points = points[indices[1:-1]]
|
||||||
|
self.discrete_path = Mobject1D(color = YELLOW)
|
||||||
|
for start, end in zip(indices, indices[1:]):
|
||||||
|
self.discrete_path.add_line(
|
||||||
|
points[start], points[end]
|
||||||
|
)
|
||||||
|
self.augmented_path = self.discrete_path.copy()
|
||||||
|
self.augmented_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):
|
||||||
|
start_y.highlight(BLACK)
|
||||||
|
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):
|
||||||
|
little_square = Square(side_length = 4, color = WHITE)
|
||||||
|
little_square.scale(0.25)
|
||||||
|
little_square.shift(self.bend_points[0])
|
||||||
|
big_square = little_square.copy()
|
||||||
|
big_square.scale(4)
|
||||||
|
big_square.to_corner(UP+RIGHT)
|
||||||
|
|
||||||
|
|
||||||
|
first_time = True
|
||||||
|
for bend_point in self.bend_points:
|
||||||
|
if first_time:
|
||||||
|
self.play(ShowCreation(little_square))
|
||||||
|
first_time = False
|
||||||
|
else:
|
||||||
|
self.remove(lines, big_square)
|
||||||
|
self.play(ApplyMethod(
|
||||||
|
little_square.shift,
|
||||||
|
bend_point - little_square.get_center()
|
||||||
|
))
|
||||||
|
lines = self.lines_connecting_squares(little_square, big_square)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(lines),
|
||||||
|
ShowCreation(big_square)
|
||||||
|
)
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def lines_connecting_squares(self, square1, square2):
|
||||||
|
return Mobject(*[
|
||||||
|
Line(
|
||||||
|
square1.get_corner(vect),
|
||||||
|
square2.get_corner(vect),
|
||||||
|
)
|
||||||
|
for vect in [UP+LEFT, DOWN+LEFT]
|
||||||
|
]).highlight(square1.get_color())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MultilayeredGlassZoomIn(Scene):
|
||||||
|
def construct(self, layer_number):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import itertools as it
|
import itertools as it
|
||||||
|
import os
|
||||||
|
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
@ -26,13 +27,96 @@ from topics.functions import ParametricFunction, FunctionGraph
|
||||||
from topics.number_line import NumberPlane
|
from topics.number_line import NumberPlane
|
||||||
from region import Region, region_from_polygon_vertices
|
from region import Region, region_from_polygon_vertices
|
||||||
from scene import Scene
|
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)
|
||||||
|
|
||||||
|
Mobject(*logos+books+[cornell]).show()
|
||||||
|
|
||||||
|
self.add(name)
|
||||||
|
self.play(FadeIn(cornell))
|
||||||
|
self.play(ShimmerIn(contributions))
|
||||||
|
for logo in logos:
|
||||||
|
self.play(FadeIn(logo))
|
||||||
|
self.play(ShimmerIn(books_word))
|
||||||
|
for book in books:
|
||||||
|
book.shift(5*LEFT)
|
||||||
|
self.play(ApplyMethod(book.shift, 5*RIGHT))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
class DisectBrachistochroneWord(Scene):
|
class DisectBrachistochroneWord(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
word = TextMobject(
|
word = TextMobject(["Bra", "chis", "to", "chrone"])
|
||||||
["Bra", "chis", "to", "chrone"]
|
|
||||||
)
|
|
||||||
original_word = word.copy()
|
original_word = word.copy()
|
||||||
dots = []
|
dots = []
|
||||||
for part in word.split():
|
for part in word.split():
|
||||||
|
@ -52,13 +136,29 @@ class DisectBrachistochroneWord(Scene):
|
||||||
time = TextMobject("Time")
|
time = TextMobject("Time")
|
||||||
time.next_to(overbrace2, UP)
|
time.next_to(overbrace2, UP)
|
||||||
time.highlight(YELLOW)
|
time.highlight(YELLOW)
|
||||||
chrono_example = TextMobject("As in ``Chronological''")
|
chrono_example = TextMobject("""
|
||||||
|
As in ``Chronological'' \\\\
|
||||||
|
or ``Synchronize''
|
||||||
|
""")
|
||||||
chrono_example.scale(0.5)
|
chrono_example.scale(0.5)
|
||||||
chrono_example.to_edge(RIGHT)
|
chrono_example.to_edge(RIGHT)
|
||||||
chrono_example.shift(2*UP)
|
chrono_example.shift(2*UP)
|
||||||
chrono_example.highlight(BLUE_D)
|
chrono_example.highlight(BLUE_D)
|
||||||
chrono_arrow = Arrow(word.split()[-1], chrono_example)
|
chrono_arrow = Arrow(
|
||||||
chrono_arrow.highlight(BLUE_D)
|
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 = TextMobject(["/br", "e", "kist","e","kr$\\bar{o}$n/"])
|
||||||
pronunciation.split()[1].rotate_in_place(np.pi)
|
pronunciation.split()[1].rotate_in_place(np.pi)
|
||||||
|
@ -94,9 +194,119 @@ class DisectBrachistochroneWord(Scene):
|
||||||
self.dither()
|
self.dither()
|
||||||
self.play(ShimmerIn(shortest))
|
self.play(ShimmerIn(shortest))
|
||||||
self.play(ShimmerIn(time))
|
self.play(ShimmerIn(time))
|
||||||
|
for ex, ar in [(chrono_example, chrono_arrow), (brachy_example, brachy_arrow)]:
|
||||||
|
self.play(
|
||||||
|
ShowCreation(ar),
|
||||||
|
ShimmerIn(ex)
|
||||||
|
)
|
||||||
self.dither()
|
self.dither()
|
||||||
self.play(
|
|
||||||
ShowCreation(chrono_arrow),
|
|
||||||
ShimmerIn(chrono_example)
|
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
|
||||||
)
|
)
|
||||||
self.dither()
|
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):
|
||||||
|
all_topics = [
|
||||||
|
TextMobject(word, size = "\\Huge")
|
||||||
|
for word in [
|
||||||
|
"Brachistochrone",
|
||||||
|
"Light through \\\\ multilayered glass",
|
||||||
|
"Light from \\\\ air into glass",
|
||||||
|
"Lifeguard problem",
|
||||||
|
"Springs and rod",
|
||||||
|
"Snell's Law",
|
||||||
|
"Cycloid",
|
||||||
|
"Mark Levi's cleverness",
|
||||||
|
]
|
||||||
|
]
|
||||||
|
brachy, multi_glass, bi_glass, lifeguard, springs, \
|
||||||
|
snell, cycloid, levi = all_topics
|
||||||
|
positions = [
|
||||||
|
(multi_glass, brachy, DOWN, "both"),
|
||||||
|
(bi_glass, multi_glass, LEFT, "to"),
|
||||||
|
(lifeguard, bi_glass, UP, "both"),
|
||||||
|
(springs, bi_glass, DOWN, "both"),
|
||||||
|
(snell, springs, RIGHT, "from"),
|
||||||
|
(cycloid, multi_glass, RIGHT, "from"),
|
||||||
|
(cycloid, snell, UP+RIGHT, "from"),
|
||||||
|
(levi, cycloid, UP, "to"),
|
||||||
|
]
|
||||||
|
arrows = []
|
||||||
|
for mob1, mob2, direction, arrow_type in positions:
|
||||||
|
hasarrow = hasattr(mob1, "arrow")
|
||||||
|
if not hasarrow:
|
||||||
|
mob1.next_to(mob2, direction, buff = 3)
|
||||||
|
arrow = Arrow(mob1, mob2)
|
||||||
|
if arrow_type in ["to", "from"]:
|
||||||
|
arrow.highlight(GREEN)
|
||||||
|
if arrow_type == "from":
|
||||||
|
arrow.rotate_in_place(np.pi)
|
||||||
|
elif arrow_type == "both":
|
||||||
|
arrow.highlight(BLUE_D)
|
||||||
|
arrow.add(arrow.copy().rotate_in_place(np.pi))
|
||||||
|
arrows.append(arrow)
|
||||||
|
if hasarrow:
|
||||||
|
mob1.arrow.add(arrow)
|
||||||
|
else:
|
||||||
|
mob1.arrow = arrow
|
||||||
|
everything = Mobject(*all_topics+arrows)
|
||||||
|
everything.scale(0.7)
|
||||||
|
everything.center()
|
||||||
|
everything.to_edge(UP)
|
||||||
|
everything.show()
|
||||||
|
|
||||||
|
self.add(brachy)
|
||||||
|
for mob in all_topics[1:]:
|
||||||
|
self.play(ApplyMethod(mob.highlight, YELLOW))
|
||||||
|
self.play(ShowCreation(mob.arrow))
|
||||||
|
self.dither()
|
||||||
|
mob.highlight(WHITE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class ImageMobject(Mobject):
|
||||||
os.path.join(IMAGE_DIR, image_file),
|
os.path.join(IMAGE_DIR, image_file),
|
||||||
os.path.join(IMAGE_DIR, image_file + ".jpg"),
|
os.path.join(IMAGE_DIR, image_file + ".jpg"),
|
||||||
os.path.join(IMAGE_DIR, image_file + ".png"),
|
os.path.join(IMAGE_DIR, image_file + ".png"),
|
||||||
|
os.path.join(IMAGE_DIR, image_file + ".gif"),
|
||||||
]
|
]
|
||||||
for path in possible_paths:
|
for path in possible_paths:
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
|
|
|
@ -1,344 +0,0 @@
|
||||||
from topics import *
|
|
||||||
from animation import *
|
|
||||||
|
|
||||||
|
|
||||||
def half_plane():
|
|
||||||
plane = NumberPlane(
|
|
||||||
x_radius = SPACE_WIDTH/2,
|
|
||||||
x_unit_to_spatial_width = 0.5,
|
|
||||||
y_unit_to_spatial_height = 0.5,
|
|
||||||
x_faded_line_frequency = 0,
|
|
||||||
y_faded_line_frequency = 0,
|
|
||||||
density = 4*DEFAULT_POINT_DENSITY_1D,
|
|
||||||
)
|
|
||||||
plane.add_coordinates(
|
|
||||||
x_vals = range(-6, 7, 2),
|
|
||||||
y_vals = range(-6, 7, 2)
|
|
||||||
)
|
|
||||||
return plane
|
|
||||||
|
|
||||||
class SingleVariableFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda x : x**2 - 3, "ShiftedSquare", True),
|
|
||||||
(lambda x : x**2 - 3, "ShiftedSquare", False),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name, separate_lines):
|
|
||||||
return name + ("SeparateLines" if separate_lines else "")
|
|
||||||
|
|
||||||
def construct(self, func, name, separate_lines):
|
|
||||||
base_line = NumberLine(color = "grey")
|
|
||||||
moving_line = NumberLine(
|
|
||||||
tick_frequency = 1,
|
|
||||||
density = 3*DEFAULT_POINT_DENSITY_1D
|
|
||||||
)
|
|
||||||
base_line.add_numbers()
|
|
||||||
def point_function((x, y, z)):
|
|
||||||
return (func(x), y, z)
|
|
||||||
target = moving_line.copy().apply_function(point_function)
|
|
||||||
|
|
||||||
transform_config = {
|
|
||||||
"run_time" : 3,
|
|
||||||
"path_func" : path_along_arc(np.pi/4)
|
|
||||||
}
|
|
||||||
|
|
||||||
if separate_lines:
|
|
||||||
numbers = moving_line.get_number_mobjects(*range(-7, 7))
|
|
||||||
negative_numbers = []
|
|
||||||
for number in numbers:
|
|
||||||
number.highlight(GREEN_E)
|
|
||||||
number.shift(-2*moving_line.get_vertical_number_offset())
|
|
||||||
center = number.get_center()
|
|
||||||
target_num = number.copy()
|
|
||||||
target_num.shift(point_function(center) - center)
|
|
||||||
target.add(target_num)
|
|
||||||
if center[0] < -0.5:
|
|
||||||
negative_numbers.append(number)
|
|
||||||
moving_line.add(*numbers)
|
|
||||||
base_line.shift(DOWN)
|
|
||||||
target.shift(DOWN)
|
|
||||||
moving_line.shift(UP)
|
|
||||||
|
|
||||||
self.add(base_line, moving_line)
|
|
||||||
self.dither(3)
|
|
||||||
self.play(Transform(moving_line, target, **transform_config))
|
|
||||||
if separate_lines:
|
|
||||||
self.play(*[
|
|
||||||
ApplyMethod(mob.shift, 0.4*UP)
|
|
||||||
for mob in negative_numbers
|
|
||||||
])
|
|
||||||
self.dither(3)
|
|
||||||
|
|
||||||
|
|
||||||
class LineToPlaneFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda x : (np.cos(x), 0.5*x*np.sin(x)), "Swirl", []),
|
|
||||||
(lambda x : (np.cos(x), 0.5*x*np.sin(x)), "Swirl", [
|
|
||||||
("0", "(1, 0)", 0),
|
|
||||||
("\\frac{\\pi}{2}", "(0, \\pi / 4)", np.pi/2),
|
|
||||||
("\\pi", "(-1, 0)", np.pi),
|
|
||||||
])
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name, numbers_to_follow):
|
|
||||||
return name + ("FollowingNumbers" if numbers_to_follow else "")
|
|
||||||
|
|
||||||
def construct(self, func, name, numbers_to_follow):
|
|
||||||
line = NumberLine(
|
|
||||||
unit_length_to_spatial_width = 0.5,
|
|
||||||
tick_frequency = 1,
|
|
||||||
number_at_center = 6,
|
|
||||||
numerical_radius = 6,
|
|
||||||
numbers_with_elongated_ticks = [0, 12],
|
|
||||||
density = 3*DEFAULT_POINT_DENSITY_1D
|
|
||||||
)
|
|
||||||
line.to_edge(LEFT)
|
|
||||||
line_copy = line.copy()
|
|
||||||
line.add_numbers(*range(0, 14, 2))
|
|
||||||
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
|
|
||||||
plane = half_plane()
|
|
||||||
plane.sub_mobjects = []
|
|
||||||
plane.filter_out(
|
|
||||||
lambda (x, y, z) : abs(x) > 0.1 and abs(y) > 0.1
|
|
||||||
)
|
|
||||||
plane.shift(0.5*SPACE_WIDTH*RIGHT)
|
|
||||||
self.add(line, divider, plane)
|
|
||||||
|
|
||||||
def point_function(point):
|
|
||||||
x, y = func(line.point_to_number(point))
|
|
||||||
return plane.num_pair_to_point((x, y))
|
|
||||||
|
|
||||||
target = line_copy.copy().apply_function(point_function)
|
|
||||||
target.highlight()
|
|
||||||
anim_config = {"run_time" : 3}
|
|
||||||
anims = [Transform(line_copy, target, **anim_config)]
|
|
||||||
|
|
||||||
colors = iter([BLUE_B, GREEN_D, RED_D])
|
|
||||||
for input_tex, output_tex, number in numbers_to_follow:
|
|
||||||
center = line.number_to_point(number)
|
|
||||||
dot = Dot(center, color = colors.next())
|
|
||||||
anims.append(ApplyMethod(
|
|
||||||
dot.shift,
|
|
||||||
point_function(center) - center,
|
|
||||||
**anim_config
|
|
||||||
))
|
|
||||||
label = TexMobject(input_tex)
|
|
||||||
label.shift(center + 2*UP)
|
|
||||||
arrow = Arrow(label, dot)
|
|
||||||
self.add(label)
|
|
||||||
self.play(ShowCreation(arrow), ShowCreation(dot))
|
|
||||||
self.dither()
|
|
||||||
self.remove(arrow, label)
|
|
||||||
|
|
||||||
|
|
||||||
self.dither(2)
|
|
||||||
self.play(*anims)
|
|
||||||
self.dither()
|
|
||||||
|
|
||||||
for input_tex, output_tex, number in numbers_to_follow:
|
|
||||||
point = plane.num_pair_to_point(func(number))
|
|
||||||
label = TexMobject(output_tex)
|
|
||||||
side_shift = LEFT if number == np.pi else RIGHT
|
|
||||||
label.shift(point, 2*UP, side_shift)
|
|
||||||
arrow = Arrow(label, point)
|
|
||||||
self.add(label)
|
|
||||||
self.play(ShowCreation(arrow))
|
|
||||||
self.dither(2)
|
|
||||||
self.remove(arrow, label)
|
|
||||||
|
|
||||||
class PlaneToPlaneFunctionSeparatePlanes(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda (x, y) : (x**2+y**2, x**2-y**2), "Quadratic")
|
|
||||||
]
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name):
|
|
||||||
return name
|
|
||||||
|
|
||||||
def construct(self, func, name):
|
|
||||||
shift_factor = 0.55
|
|
||||||
in_plane = half_plane().shift(shift_factor*SPACE_WIDTH*LEFT)
|
|
||||||
out_plane = half_plane().shift(shift_factor*SPACE_WIDTH*RIGHT)
|
|
||||||
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
|
|
||||||
self.add(in_plane, out_plane, divider)
|
|
||||||
|
|
||||||
plane_copy = in_plane.copy()
|
|
||||||
plane_copy.sub_mobjects = []
|
|
||||||
|
|
||||||
def point_function(point):
|
|
||||||
result = np.array(func((point*2 + 2*shift_factor*SPACE_WIDTH*RIGHT)[:2]))
|
|
||||||
result = np.append(result/2, [0])
|
|
||||||
return result + shift_factor*SPACE_WIDTH*RIGHT
|
|
||||||
|
|
||||||
target = plane_copy.copy().apply_function(point_function)
|
|
||||||
target.highlight(GREEN_B)
|
|
||||||
|
|
||||||
anim_config = {"run_time" : 5}
|
|
||||||
|
|
||||||
self.dither()
|
|
||||||
self.play(Transform(plane_copy, target, **anim_config))
|
|
||||||
self.dither()
|
|
||||||
|
|
||||||
class PlaneToPlaneFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda (x, y) : (x**2+y**2, x**2-y**2), "Quadratic")
|
|
||||||
]
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name):
|
|
||||||
return name
|
|
||||||
|
|
||||||
def construct(self, func, name):
|
|
||||||
plane = NumberPlane()
|
|
||||||
background = NumberPlane(color = "grey")
|
|
||||||
background.add_coordinates()
|
|
||||||
anim_config = {"run_time" : 3}
|
|
||||||
|
|
||||||
def point_function(point):
|
|
||||||
return np.append(func(point[:2]), [0])
|
|
||||||
|
|
||||||
self.add(background, plane)
|
|
||||||
self.dither(2)
|
|
||||||
self.play(ApplyPointwiseFunction(point_function, plane, **anim_config))
|
|
||||||
self.dither(3)
|
|
||||||
|
|
||||||
class PlaneToLineFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda (x, y) : x**2 + y**2, "Bowl"),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name):
|
|
||||||
return name
|
|
||||||
|
|
||||||
def construct(self, func, name):
|
|
||||||
line = NumberLine(
|
|
||||||
color = GREEN,
|
|
||||||
unit_length_to_spatial_width = 0.5,
|
|
||||||
tick_frequency = 1,
|
|
||||||
number_at_center = 6,
|
|
||||||
numerical_radius = 6,
|
|
||||||
numbers_with_elongated_ticks = [0, 12],
|
|
||||||
).to_edge(RIGHT)
|
|
||||||
line.add_numbers()
|
|
||||||
plane = half_plane().to_edge(LEFT, buff = 0)
|
|
||||||
|
|
||||||
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
|
|
||||||
line_left = line.number_to_point(0)
|
|
||||||
def point_function(point):
|
|
||||||
shifter = 0.5*SPACE_WIDTH*RIGHT
|
|
||||||
return func((point+shifter)[:2])*RIGHT + line_left
|
|
||||||
|
|
||||||
self.add(line, plane, divider)
|
|
||||||
self.dither()
|
|
||||||
plane.sub_mobjects = []
|
|
||||||
self.play(ApplyPointwiseFunction(point_function, plane))
|
|
||||||
self.dither()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PlaneToSpaceFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda (x, y) : (x*x, x*y, y*y), "Quadratic"),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name):
|
|
||||||
return name
|
|
||||||
|
|
||||||
def construct(self, func, name):
|
|
||||||
plane = half_plane().shift(0.5*SPACE_WIDTH*LEFT)
|
|
||||||
divider = Line(SPACE_HEIGHT*UP, SPACE_HEIGHT*DOWN)
|
|
||||||
axes = XYZAxes()
|
|
||||||
axes.filter_out(lambda p : np.linalg.norm(p) > 3)
|
|
||||||
rot_kwargs = {
|
|
||||||
"run_time" : 3,
|
|
||||||
"radians" : 0.3*np.pi,
|
|
||||||
"axis" : [0.1, 1, 0.1],
|
|
||||||
}
|
|
||||||
axes.to_edge(RIGHT).shift(DOWN)
|
|
||||||
dampening_factor = 0.1
|
|
||||||
def point_function((x, y, z)):
|
|
||||||
return dampening_factor*np.array(func((x, y)))
|
|
||||||
target = NumberPlane().apply_function(point_function)
|
|
||||||
target.highlight("yellow")
|
|
||||||
target.shift(axes.get_center())
|
|
||||||
|
|
||||||
self.add(plane, divider, axes)
|
|
||||||
self.play(Rotating(axes, **rot_kwargs))
|
|
||||||
|
|
||||||
target.rotate_in_place(rot_kwargs["radians"])
|
|
||||||
self.play(
|
|
||||||
TransformAnimations(
|
|
||||||
Animation(plane.copy()),
|
|
||||||
Rotating(target, **rot_kwargs),
|
|
||||||
rate_func = smooth
|
|
||||||
),
|
|
||||||
Rotating(axes, **rot_kwargs)
|
|
||||||
)
|
|
||||||
axes.add(target)
|
|
||||||
self.clear()
|
|
||||||
self.add(plane, divider, axes)
|
|
||||||
self.play(Rotating(axes, **rot_kwargs))
|
|
||||||
self.clear()
|
|
||||||
for i in range(5):
|
|
||||||
self.play(Rotating(axes, **rot_kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
class SpaceToSpaceFunction(Scene):
|
|
||||||
args_list = [
|
|
||||||
(lambda (x, y, z) : (y*z, x*z, x*y), "Quadratic"),
|
|
||||||
]
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def args_to_string(func, name):
|
|
||||||
return name
|
|
||||||
|
|
||||||
def construct(self, func, name):
|
|
||||||
space = SpaceGrid()
|
|
||||||
rot_kwargs = {
|
|
||||||
"run_time" : 10,
|
|
||||||
"radians" : 2*np.pi/5,
|
|
||||||
"axis" : [0.1, 1, 0.1],
|
|
||||||
"in_place" : False,
|
|
||||||
}
|
|
||||||
axes = XYZAxes()
|
|
||||||
target = space.copy().apply_function(func)
|
|
||||||
|
|
||||||
self.play(
|
|
||||||
TransformAnimations(
|
|
||||||
Rotating(space, **rot_kwargs),
|
|
||||||
Rotating(target, **rot_kwargs),
|
|
||||||
rate_func = squish_rate_func(smooth, 0.3, 0.7)
|
|
||||||
),
|
|
||||||
Rotating(axes, **rot_kwargs)
|
|
||||||
)
|
|
||||||
axes.add(space)
|
|
||||||
self.play(Rotating(axes, **rot_kwargs))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue