More scenes for ode1

This commit is contained in:
Grant Sanderson 2019-03-26 17:52:53 -07:00
parent 9a452f3fee
commit caf41f43d9
4 changed files with 563 additions and 50 deletions

View file

@ -17,7 +17,8 @@ ALL_SCENE_CLASSES = [
SmallAngleApproximationTex,
VeryLowAnglePendulum,
FormulasAreLies,
FollowThisThread,
TourOfDifferentialEquations,
# FollowThisThread,
StrogatzQuote,
# Something...
ShowGravityAcceleration,
@ -40,6 +41,11 @@ ALL_SCENE_CLASSES = [
FromODEToVectorField,
LorenzVectorField,
ThreeBodiesInSpace,
AltThreeBodiesInSpace,
ThreeBodySymbols,
AskAboutActuallySolving,
WriteODESolvingCode,
TakeManyTinySteps,
InaccurateComputation,
HungerForExactness,
]

View file

@ -1113,3 +1113,127 @@ class TweakMuInVectorField(ShowPendulumPhaseFlow):
)
self.add(animated_stream_lines)
self.wait(self.flow_time)
class TakeManyTinySteps(IntroduceVectorField):
CONFIG = {
"initial_theta": 60 * DEGREES,
"initial_theta_dot": 0,
}
def construct(self):
self.initialize_plane()
self.initialize_vector_field()
field = self.vector_field
field.set_opacity(0.35)
self.add(self.plane, field)
self.take_many_time_steps()
def take_many_time_steps(self):
delta_t_tracker = ValueTracker(0.5)
get_delta_t = delta_t_tracker.get_value
time_tracker = ValueTracker(10)
get_t = time_tracker.get_value
traj = always_redraw(
lambda: self.get_time_step_trajectory(
get_delta_t(), get_t()
)
)
vectors = always_redraw(
lambda: self.get_path_vectors(
get_delta_t(), get_t()
)
)
t_label, dt_label = labels = VGroup(*[
VGroup(
TexMobject("{} = ".format(s)),
DecimalNumber(0)
).arrange(RIGHT, aligned_edge=DOWN)
for s in ("t", "{\\Delta t}")
])
init_labels = VGroup(
TexMobject(
"\\theta_0", "= \\pi / 3",
tex_to_color_map={"\\theta": BLUE},
),
TexMobject(
"{\\dot\\theta}_0 = 0",
tex_to_color_map={"{\\dot\\theta}": YELLOW},
),
)
for group in labels, init_labels:
for label in group:
label.scale(1.25)
label.add_background_rectangle()
group.arrange(DOWN)
group.shift(FRAME_WIDTH * RIGHT / 4)
labels.to_edge(UP)
init_labels.shift(2 * DOWN)
dt_label[-1].add_updater(
lambda d: d.set_value(get_delta_t())
)
t_label[-1].add_updater(
lambda d: d.set_value(
int(get_t() / get_delta_t()) * get_delta_t()
)
)
self.add(traj, vectors, init_labels, labels)
time_tracker.set_value(0)
self.play(
time_tracker.set_value, 10,
run_time=5,
rate_func=linear,
)
self.wait()
t_label[-1].clear_updaters()
self.play(
delta_t_tracker.set_value, 0.01,
run_time=7,
)
self.wait()
#
def get_time_step_points(self, delta_t, total_time):
plane = self.plane
field = self.vector_field
curr_point = plane.coords_to_point(
self.initial_theta,
self.initial_theta_dot,
)
points = [curr_point]
t = 0
while t < total_time:
new_point = curr_point + field.func(curr_point) * delta_t
points.append(new_point)
curr_point = new_point
t += delta_t
return points
def get_time_step_trajectory(self, delta_t, total_time):
traj = VMobject()
traj.set_points_as_corners(
self.get_time_step_points(delta_t, total_time)
)
traj.set_stroke(WHITE, 2)
return traj
def get_path_vectors(self, delta_t, total_time):
corners = self.get_time_step_points(
delta_t, total_time
)
result = VGroup()
for a1, a2 in zip(corners, corners[1:]):
vector = Arrow(
a1, a2, buff=0,
)
vector.match_style(
self.vector_field.get_vector(a1)
)
result.add(vector)
return result

View file

@ -103,6 +103,11 @@ class FormulasAreLies(PiCreatureScene):
return You().flip().to_corner(DR)
# class TourOfDifferentialEquations(Scene):
# def construct(self):
# pass
class ProveTeacherWrong(TeacherStudentsScene):
def construct(self):
tex_config = {
@ -197,15 +202,183 @@ class ProveTeacherWrong(TeacherStudentsScene):
self.wait(8)
class AskAboutActuallySolving(Scene):
class AskAboutActuallySolving(TeacherStudentsScene):
def construct(self):
ode = get_ode()
ode.to_corner(UL)
self.add(ode)
morty = self.teacher
self.student_says(
"Yeah, yeah, but how do\\\\"
"Yeah yeah, but how do\\\\"
"you acutally \\emph{solve} it?",
student_index=1,
target_mode="sassy",
added_anims=[morty.change, "thinking"],
)
self.change_student_modes(
"confused", "sassy", "confused",
look_at_arg=ode,
)
self.wait()
self.teacher_says(
"What do you mean\\\\ by ``solve''?",
target_mode="speaking",
added_anims=[self.get_student_changes(
*3 * ["erm"]
)]
)
self.play(self.students[1].change, "angry")
self.wait(3)
class HungerForExactness(TeacherStudentsScene):
def construct(self):
students = self.students
you = students[2]
teacher = self.teacher
ode = get_ode()
ode.to_corner(UL)
left_part = ode[:5]
friction_part = ode[5:11]
right_part = ode[11:]
self.add(ode)
frictionless_group = VGroup(left_part, right_part)
proposed_solution = TexMobject(
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
)
proposed_solution.next_to(
you.get_corner(UL), UP, buff=0.7
)
proposed_solution_rect = SurroundingRectangle(
proposed_solution, buff=MED_SMALL_BUFF,
)
proposed_solution_rect.set_color(BLUE)
proposed_solution_rect.round_corners()
solution_p1 = TexMobject(
"""
\\theta(t) = 2\\text{am}\\left(
\\frac{\\sqrt{2g + Lc_1} (t + c_2)}{2\\sqrt{L}},
\\frac{4g}{2g + Lc_1}
\\right)
""",
)
solution_p1.to_corner(UL)
solution_p2 = TexMobject(
"c_1, c_2 = \\text{Constants depending on initial conditions}"
)
solution_p2.set_color(LIGHT_GREY)
solution_p2.scale(0.75)
solution_p3 = TexMobject(
"""
\\text{am}(u, k) =
\\int_0^u \\text{dn}(v, k)\\,dv
"""
)
solution_p3.name = TextMobject(
"(Jacobi amplitude function)"
)
solution_p4 = TexMobject(
"""
\\text{dn}(u, k) =
\\sqrt{1 - k^2 \\sin^2(\\phi)}
"""
)
solution_p4.name = TextMobject(
"(Jacobi elliptic function)"
)
solution_p5 = TextMobject("Where $\\phi$ satisfies")
solution_p6 = TexMobject(
"""
u = \\int_0^\\phi \\frac{dt}{\\sqrt{1 - k^2 \\sin^2(t)}}
"""
)
solution = VGroup(
solution_p1,
solution_p2,
solution_p3,
solution_p4,
solution_p5,
solution_p6,
)
solution.arrange(DOWN)
solution.scale(0.7)
solution.to_corner(UL, buff=MED_SMALL_BUFF)
solution.set_stroke(width=0, background=True)
solution.remove(solution_p2)
solution_p1.add(solution_p2)
solution.remove(solution_p5)
solution_p6.add(solution_p5)
for part in [solution_p3, solution_p4]:
part.name.scale(0.7 * 0.7)
part.name.set_color(LIGHT_GREY)
part.name.next_to(part, RIGHT)
part.add(part.name)
self.student_says(
"Right, but like,\\\\"
"what \\emph{is} $\\theta(t)$?",
target_mode="sassy",
added_anims=[teacher.change, "guilty"],
)
self.wait()
self.play(
FadeInFromDown(proposed_solution),
RemovePiCreatureBubble(
you,
target_mode="raise_left_hand",
look_at_arg=proposed_solution,
),
teacher.change, "pondering",
students[0].change, "pondering",
students[1].change, "hesitant",
)
self.play(ShowCreation(proposed_solution_rect))
self.play(
proposed_solution.shift, 3 * RIGHT,
proposed_solution_rect.shift, 3 * RIGHT,
you.change, "raise_right_hand", teacher.eyes,
)
self.wait(3)
self.play(
FadeOut(proposed_solution),
FadeOut(proposed_solution_rect),
ode.move_to, self.hold_up_spot, DOWN,
ode.shift, LEFT,
teacher.change, "raise_right_hand",
self.get_student_changes(*3 * ["pondering"])
)
self.wait()
ode.save_state()
self.play(
left_part.move_to, friction_part, RIGHT,
left_part.match_y, left_part,
friction_part.to_corner, DR,
friction_part.fade, 0.5,
)
self.wait()
modes = ["erm", "sad", "sad", "horrified"]
for part, mode in zip(solution, modes):
self.play(
FadeInFrom(part, UP),
self.get_student_changes(
*3 * [mode],
look_at_arg=part,
)
)
self.wait()
self.wait(3)
self.play(
FadeOutAndShift(solution, 2 * LEFT),
Restore(ode),
self.get_student_changes(*3 * ["sick"])
)
self.wait(3)

View file

@ -39,28 +39,37 @@ class VectorFieldTest(Scene):
self.wait(10)
class FollowThisThread(Scene):
class TourOfDifferentialEquations(MovingCameraScene):
CONFIG = {
"screen_rect_style": {
"stroke_width": 2,
"stroke_color": WHITE,
"fill_opacity": 1,
"fill_color": DARKER_GREY,
}
"fill_color": BLACK,
},
"camera_config": {"background_color": DARKER_GREY},
}
def construct(self):
self.add_title()
self.show_thumbnails()
self.show_words()
# self.show_words()
def add_title(self):
title = TextMobject(
"A Tourist's Guide \\\\to Differential\\\\Equations"
)
title.scale(1.5)
title.to_corner(UR)
self.add(title)
def show_thumbnails(self):
# TODO, replace each of these with a picture?
thumbnails = self.thumbnails = VGroup(
ScreenRectangle(**self.screen_rect_style),
ScreenRectangle(**self.screen_rect_style),
ScreenRectangle(**self.screen_rect_style),
ScreenRectangle(**self.screen_rect_style),
ScreenRectangle(**self.screen_rect_style),
thumbnails = self.thumbnails = Group(
Group(ScreenRectangle(**self.screen_rect_style)),
Group(ScreenRectangle(**self.screen_rect_style)),
Group(ScreenRectangle(**self.screen_rect_style)),
Group(ScreenRectangle(**self.screen_rect_style)),
Group(ScreenRectangle(**self.screen_rect_style)),
)
n = len(thumbnails)
thumbnails.set_height(1.5)
@ -71,8 +80,16 @@ class FollowThisThread(Scene):
[-3, -3, 0],
[5, -3, 0],
])
line.shift(MED_SMALL_BUFF * LEFT)
for thumbnail, a in zip(thumbnails, np.linspace(0, 1, n)):
thumbnail.move_to(line.point_from_proportion(a))
dots = TexMobject("\\dots")
dots.next_to(thumbnails[-1], RIGHT)
self.add_heat_preview(thumbnails[1])
self.add_fourier_series(thumbnails[2])
self.add_matrix_exponent(thumbnails[3])
self.add_laplace_symbol(thumbnails[4])
self.play(
ShowCreation(
@ -91,6 +108,14 @@ class FollowThisThread(Scene):
], lag_ratio=1),
run_time=5
)
self.play(Write(dots))
self.wait()
self.play(
self.camera_frame.replace,
thumbnails[0],
run_time=3,
)
self.wait()
def show_words(self):
words = VGroup(
@ -123,6 +148,71 @@ class FollowThisThread(Scene):
)
self.wait()
#
def add_heat_preview(self, thumbnail):
image = ImageMobject("HeatSurfaceExample")
image.replace(thumbnail)
thumbnail.add(image)
def add_matrix_exponent(self, thumbnail):
matrix = IntegerMatrix(
[[3, 1], [4, 1]],
v_buff=MED_LARGE_BUFF,
h_buff=MED_LARGE_BUFF,
bracket_h_buff=SMALL_BUFF,
bracket_v_buff=SMALL_BUFF,
)
e = TexMobject("e")
t = TexMobject("t")
t.scale(1.5)
t.next_to(matrix, RIGHT, SMALL_BUFF)
e.scale(2)
e.move_to(matrix.get_corner(DL), UR)
group = VGroup(e, matrix, t)
group.set_height(0.7 * thumbnail.get_height())
randy = Randolph(mode="confused", height=0.75)
randy.next_to(group, LEFT, aligned_edge=DOWN)
randy.look_at(matrix)
group.add(randy)
group.move_to(thumbnail)
thumbnail.add(group)
def add_fourier_series(self, thumbnail):
colors = [BLUE, GREEN, YELLOW, RED, RED_E, PINK]
waves = VGroup(*[
self.get_square_wave_approx(N, color)
for N, color in enumerate(colors)
])
waves.set_stroke(width=1.5)
waves.replace(thumbnail, stretch=True)
waves.scale(0.8)
waves.move_to(thumbnail)
thumbnail.add(waves)
def get_square_wave_approx(self, N, color):
return FunctionGraph(
lambda x: sum([
(1 / n) * np.sin(n * PI * x)
for n in range(1, 2 * N + 3, 2)
]),
x_min=0,
x_max=2,
color=color
)
def add_laplace_symbol(self, thumbnail):
mob = TexMobject(
"\\mathcal{L}\\left\\{f(t)\\right\\}"
)
mob.set_width(0.8 * thumbnail.get_width())
mob.move_to(thumbnail)
thumbnail.add(mob)
class HeatEquationPreview(ExternallyAnimatedScene):
pass
class ShowGravityAcceleration(Scene):
def construct(self):
@ -395,15 +485,7 @@ class DefineODE(Scene):
de_word = TextMobject("Differential", "Equation")
de_word.to_edge(UP)
equation = TexMobject(
"\\ddot \\theta({t})", "=",
"-\\mu \\dot \\theta({t})",
"-{g \\over L} \\sin\\big(\\theta({t})\\big)",
tex_to_color_map={
"\\theta": BLUE,
"{t}": WHITE
}
)
equation = get_ode()
equation.next_to(de_word, DOWN)
thetas = equation.get_parts_by_tex("\\theta")
@ -475,11 +557,18 @@ class DefineODE(Scene):
)
tex_config = {
"tex_to_color_map": {"\\theta": BLUE},
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": YELLOW,
"{\\ddot\\theta}": RED,
},
"height": 0.5,
}
theta, d_theta, dd_theta = [
TexMobject(s + "\\theta(t)", **tex_config)
TexMobject(
"{" + s + "\\theta}(t)",
**tex_config
)
for s in ("", "\\dot", "\\ddot")
]
@ -500,7 +589,7 @@ class DefineODE(Scene):
return DashedLine(
x_point, point,
dash_length=0.025,
stroke_color=WHITE,
stroke_color=BLUE,
stroke_width=2,
)
@ -545,7 +634,7 @@ class DefineODE(Scene):
thetas = VGroup(theta, d_theta, dd_theta)
words = VGroup(
TextMobject("= Height"),
TextMobject("= Height").set_color(BLUE),
TextMobject("= Slope").set_color(YELLOW),
TextMobject("= ``Curvature''").set_color(RED),
)
@ -887,7 +976,8 @@ class LorenzVectorField(ExternallyAnimatedScene):
class ThreeBodiesInSpace(SpecialThreeDScene):
CONFIG = {
"masses": [1, 2, 3],
"masses": [1, 6, 3],
"colors": [RED_E, GREEN_E, BLUE_E],
"G": 0.5,
"play_time": 60,
}
@ -900,26 +990,48 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
def add_axes(self):
axes = self.axes = self.get_axes()
axes.set_stroke(width=0.5)
self.add(axes)
def add_bodies(self):
masses = self.masses
colors = self.colors
bodies = self.bodies = VGroup()
velocity_vectors = VGroup()
for mass in self.masses:
body = self.get_sphere(
checkerboard_colors=[DARK_BROWN, DARK_BROWN],
stroke_width=0.1,
)
body.mass = mass
body.set_width(0.2 * mass)
point = np.dot(
2 * (np.random.random(3) - 0.5),
centers = [
np.dot(
4 * (np.random.random(3) - 0.5),
[RIGHT, UP, OUT]
)
velocity = normalize(np.cross(point, OUT))
body.move_to(point)
for x in range(len(masses))
]
for mass, color, center in zip(masses, colors, centers):
body = self.get_sphere(
checkerboard_colors=[
color, color
],
color=color,
stroke_width=0.1,
)
body.set_opacity(0.75)
body.mass = mass
body.set_width(0.15 * np.sqrt(mass))
body.point = center
body.move_to(center)
to_others = [
center - center2
for center2 in centers
]
velocity = 0.2 * mass * normalize(np.cross(*filter(
lambda diff: get_norm(diff) > 0,
to_others
)))
body.velocity = velocity
body.add_updater(self.update_body)
@ -946,27 +1058,28 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
def add_trajectories(self):
def update_trajectory(traj, dt):
new_point = traj.body.get_center()
new_point = traj.body.point
if get_norm(new_point - traj.points[-1]) > 0.01:
traj.add_smooth_curve_to(new_point)
for body in self.bodies:
traj = VMobject()
traj.body = body
traj.start_new_path(body.get_center())
traj.set_stroke(WHITE, 1)
traj.start_new_path(body.point)
traj.set_stroke(body.color, 1, opacity=0.75)
traj.add_updater(update_trajectory)
self.add(traj, body)
def let_play(self):
self.move_camera(
self.set_camera_orientation(
phi=70 * DEGREES,
theta=-110 * DEGREES,
run_time=3,
)
self.begin_ambient_camera_rotation()
for x in range(6):
self.wait(self.play_time / 6)
# Break it up to see partial files as
# it's rendered
for x in range(int(self.play_time)):
self.wait()
#
def get_velocity_vector_mob(self, body):
@ -994,13 +1107,110 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
for body2 in self.bodies:
if body2 is body:
continue
diff = body2.get_center() - body.get_center()
diff = body2.point - body.point
m2 = body2.mass
R = get_norm(diff)
acceleration += G * m2 * diff / (R**3)
body.shift(body.velocity * dt)
body.velocity += acceleration * dt
num_mid_steps = 100
for x in range(num_mid_steps):
body.point += body.velocity * dt / num_mid_steps
body.velocity += acceleration * dt / num_mid_steps
body.move_to(body.point)
return body
class AltThreeBodiesInSpace(ThreeBodiesInSpace):
CONFIG = {
"random_seed": 6,
"masses": [1, 2, 6],
}
class DefineODECopy(DefineODE):
pass
class WriteODESolvingCode(ExternallyAnimatedScene):
pass
class InaccurateComputation(Scene):
def construct(self):
h_line = DashedLine(LEFT_SIDE, RIGHT_SIDE)
h_line.to_edge(UP, buff=1.5)
words = VGroup(
TextMobject("Real number"),
TextMobject("IEEE 754\\\\representation"),
TextMobject("Error"),
)
for i, word in zip([-1, 0, 1], words):
word.next_to(h_line, UP)
word.shift(i * FRAME_WIDTH * RIGHT / 3)
lines = VGroup(*[
DashedLine(TOP, BOTTOM)
for x in range(4)
])
lines.arrange(RIGHT)
lines.stretch_to_fit_width(FRAME_WIDTH)
self.add(h_line, lines[1:-1], words)
numbers = VGroup(
TexMobject("\\pi").scale(2),
TexMobject("e^{\\sqrt{163}\\pi}").scale(1.5),
)
numbers.set_color(YELLOW)
numbers.set_stroke(width=0, background=True)
bit_strings = VGroup(
TexMobject(
"01000000",
"01001001",
"00001111",
"11011011",
),
TexMobject(
"01011100",
"01101001",
"00101110",
"00011001",
)
)
for mob in bit_strings:
mob.arrange(DOWN, buff=SMALL_BUFF)
for word in mob:
for submob, bit in zip(word, word.get_tex_string()):
if bit == "0":
submob.set_color(LIGHT_GREY)
errors = VGroup(
TexMobject(
"\\approx 8.7422 \\times 10^{-8}"
),
TexMobject(
"\\approx 5{,}289{,}803{,}032.00",
),
)
errors.set_color(RED)
content = VGroup(numbers, bit_strings, errors)
for group, word in zip(content, words):
group[1].shift(3 * DOWN)
group.move_to(DOWN)
group.match_x(word)
self.play(*map(Write, numbers))
self.wait()
self.play(
TransformFromCopy(numbers, bit_strings),
lag_ratio=0.01,
run_time=2,
)
self.wait()
self.play(FadeInFrom(errors, 3 * LEFT))
self.wait()
class NewSceneName(Scene):