Pile of ODE scenes

This commit is contained in:
Grant Sanderson 2019-03-29 15:12:38 -07:00
parent e8fe67650a
commit 9d566de8d1
7 changed files with 1178 additions and 304 deletions

View file

@ -40,6 +40,18 @@ ALL_SCENE_CLASSES = [
ODEvsPDEinFrames,
ProveTeacherWrong,
SetAsideSeekingSolution,
#
WriteInRadians,
XEqLThetaToCorner,
ComingUp,
InputLabel,
SoWhatIsThetaThen,
ReallyHardToSolve,
ReasonForSolution,
GleickQuote,
SpectrumOfStartingStates,
WritePhaseFlow,
AskAboutStability,
# VisualizeHeightSlopeCurvature,
VisualizeStates,
ReferencePiCollisionStateSpaces,
@ -54,11 +66,18 @@ ALL_SCENE_CLASSES = [
LorenzVectorField,
ThreeBodiesInSpace,
AltThreeBodiesInSpace,
TwoBodiesInSpace,
TwoBodiesWithZPart,
ThreeBodyTitle,
ThreeBodySymbols,
#
HighAmplitudePendulum,
WritePhaseSpace,
#
AskAboutActuallySolving,
WriteODESolvingCode,
TakeManyTinySteps,
ManyStepsFromDifferentStartingPoints,
InaccurateComputation,
HungerForExactness,
ShowRect,

View file

@ -514,6 +514,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
Write(formula),
hm_word.to_corner, UR
)
self.wait(4)
def show_graph_period(self):
pendulum = self.pendulum
@ -1002,19 +1003,20 @@ class AnalyzePendulumForce(MovingCameraScene):
def construct(self):
self.add_pendulum()
self.show_arc_length()
self.add_g_vect()
self.show_constraint()
self.break_g_vect_into_components()
self.show_gsin_formula()
self.show_acceleration_at_different_angles()
self.show_angle_geometry()
self.ask_about_what_to_do()
self.show_gsin_formula()
self.show_sign()
self.show_acceleration_formula()
# self.ask_about_what_to_do()
self.emphasize_theta()
self.show_arc_length()
self.show_angular_velocity()
self.show_angular_acceleration()
self.circle_g_sin_formula()
# self.emphasize_theta()
# self.show_angular_velocity()
# self.show_angular_acceleration()
# self.circle_g_sin_formula()
def add_pendulum(self):
pendulum = Pendulum(**self.pendulum_config)
@ -1027,6 +1029,97 @@ class AnalyzePendulumForce(MovingCameraScene):
self.pendulum = pendulum
self.theta_tracker = theta_tracker
def show_arc_length(self):
pendulum = self.pendulum
angle = pendulum.get_theta()
height = pendulum.length
top = pendulum.get_fixed_point()
line = Line(UP, DOWN)
line.set_height(height)
line.move_to(top, UP)
arc = always_redraw(lambda: Arc(
start_angle=-90 * DEGREES,
angle=pendulum.get_theta(),
arc_center=pendulum.get_fixed_point(),
radius=pendulum.length,
stroke_color=GREEN,
))
brace = Brace(Line(ORIGIN, 5 * UP), RIGHT)
brace.point = VectorizedPoint(brace.get_right())
brace.add(brace.point)
brace.set_height(angle)
brace.move_to(ORIGIN, DL)
brace.apply_complex_function(np.exp)
brace.scale(height)
brace.rotate(-90 * DEGREES)
brace.move_to(arc)
brace.shift(MED_SMALL_BUFF * normalize(
arc.point_from_proportion(0.5) - top
))
x_sym = TexMobject("x")
x_sym.set_color(GREEN)
x_sym.next_to(brace.point, DR, buff=SMALL_BUFF)
rhs = TexMobject("=", "L", "\\theta")
rhs.set_color_by_tex("\\theta", BLUE)
rhs.next_to(x_sym, RIGHT)
rhs.shift(0.7 * SMALL_BUFF * UP)
line_L = TexMobject("L")
line_L.next_to(
pendulum.rod.get_center(), UR, SMALL_BUFF,
)
self.play(
ShowCreation(arc),
Rotate(line, angle, about_point=top),
UpdateFromAlphaFunc(
line, lambda m, a: m.set_stroke(
width=2 * there_and_back(a)
)
),
GrowFromPoint(
brace, line.get_bottom(),
path_arc=angle
),
)
self.play(FadeInFrom(x_sym, UP))
self.wait()
# Show equation
line.set_stroke(BLUE, 5)
self.play(
ShowCreationThenFadeOut(line),
FadeInFromDown(line_L)
)
self.play(
TransformFromCopy(
line_L, rhs.get_part_by_tex("L")
),
Write(rhs.get_part_by_tex("="))
)
self.play(
TransformFromCopy(
pendulum.theta_label,
rhs.get_parts_by_tex("\\theta"),
)
)
self.add(rhs)
x_eq = VGroup(x_sym, rhs)
self.play(
FadeOut(brace),
x_eq.rotate, angle / 2,
x_eq.next_to, arc.point_from_proportion(0.5),
UL, {"buff": -MED_SMALL_BUFF}
)
self.x_eq = x_eq
self.arc = arc
self.line_L = line_L
def add_g_vect(self):
pendulum = self.pendulum
@ -1044,15 +1137,10 @@ class AnalyzePendulumForce(MovingCameraScene):
GrowArrow(g_vect),
FadeInFrom(g_word, UP, lag_ratio=0.1),
)
self.wait()
def show_constraint(self):
pendulum = self.pendulum
weight = pendulum.weight
p = weight.get_center()
path = CubicBezier([p, p + 3 * DOWN, p + 3 * UP, p])
self.play(MoveAlongPath(weight, path, run_time=2))
self.wait()
arcs = VGroup()
for u in [-1, 2, -1]:
@ -1063,7 +1151,7 @@ class AnalyzePendulumForce(MovingCameraScene):
radius=pendulum.length,
arc_center=pendulum.get_fixed_point(),
stroke_width=2,
stroke_color=GREEN,
stroke_color=YELLOW,
stroke_opacity=0.5,
)
self.play(
@ -1071,9 +1159,7 @@ class AnalyzePendulumForce(MovingCameraScene):
ShowCreation(arc)
)
arcs.add(arc)
self.wait()
self.traced_arcs = arcs
self.play(FadeOut(arcs))
def break_g_vect_into_components(self):
g_vect = self.g_vect
@ -1100,6 +1186,52 @@ class AnalyzePendulumForce(MovingCameraScene):
self.play(GrowArrow(g_vect.perp))
self.wait()
def show_angle_geometry(self):
g_vect = self.g_vect
arc = Arc(
start_angle=90 * DEGREES,
angle=self.pendulum.get_theta(),
radius=0.5,
arc_center=g_vect.get_end(),
)
q_mark = TexMobject("?")
q_mark.next_to(arc.get_center(), UL, SMALL_BUFF)
theta_label = TexMobject("\\theta")
theta_label.move_to(q_mark)
self.add(g_vect)
self.play(
ShowCreation(arc),
Write(q_mark)
)
self.play(ShowCreationThenFadeAround(q_mark))
self.wait()
self.play(ShowCreationThenFadeAround(
self.pendulum.theta_label
))
self.play(
TransformFromCopy(
self.pendulum.theta_label,
theta_label,
),
FadeOut(q_mark)
)
self.wait()
self.play(WiggleOutThenIn(g_vect.tangent))
self.play(WiggleOutThenIn(
Line(
*g_vect.get_start_and_end(),
buff=0,
).add_tip().match_style(g_vect),
remover=True
))
self.wait()
self.play(
FadeOut(arc),
FadeOut(theta_label),
)
def show_gsin_formula(self):
g_vect = self.g_vect
g_word = self.g_word
@ -1154,27 +1286,7 @@ class AnalyzePendulumForce(MovingCameraScene):
self.g_sin_label = g_sin_label
self.g_cos_label = g_cos_label
def show_acceleration_at_different_angles(self):
to_fade = VGroup(
self.g_cos_label,
self.g_vect.perp,
)
new_comp_line_sytle = {
"stroke_width": 0.5,
"stroke_opacity": 0.25,
}
self.play(
to_fade.set_opacity, 0.25,
self.g_vect.component_lines.set_style,
new_comp_line_sytle
)
self.g_vect.component_lines.add_updater(
lambda m: m.set_style(**new_comp_line_sytle)
)
for mob in to_fade:
mob.add_updater(lambda m: m.set_opacity(0.25))
def show_sign(self):
get_theta = self.pendulum.get_theta
theta_decimal = DecimalNumber(include_sign=True)
theta_decimal.add_updater(lambda d: d.set_value(
@ -1187,84 +1299,148 @@ class AnalyzePendulumForce(MovingCameraScene):
GREEN if get_theta() > 0 else RED
))
self.play(
FadeInFrom(theta_decimal, UP),
FadeOut(self.x_eq),
FadeOut(self.line_L),
)
self.set_theta(-60 * DEGREES, run_time=4)
self.set_theta(60 * DEGREES, run_time=4)
self.play(
FadeOut(theta_decimal),
FadeIn(self.x_eq),
)
def show_acceleration_formula(self):
x_eq = self.x_eq
g_sin_theta = self.g_sin_label
equation = TexMobject(
"a", "=",
"\\ddot", "x",
"=",
"-", "g", "\\sin\\big(", "\\theta", "\\big)",
)
equation.to_edge(LEFT)
second_deriv = equation[2:4]
x_part = equation.get_part_by_tex("x")
x_part.set_color(GREEN)
a_eq = equation[:2]
eq2 = equation.get_parts_by_tex("=")[1]
rhs = equation[5:]
second_deriv_L_form = TexMobject(
"L", "\\ddot", "\\theta"
)
second_deriv_L_form.move_to(second_deriv, DOWN)
eq3 = TexMobject("=")
eq3.rotate(90 * DEGREES)
eq3.next_to(second_deriv_L_form, UP)
g_L_frac = TexMobject(
"-", "{g", "\\over", "L}"
)
g_L_frac.move_to(rhs[:2], LEFT)
g_L_frac.shift(SMALL_BUFF * UP / 2)
mu_term = TexMobject(
"-\\mu", "\\dot", "\\theta",
)
mu_term.next_to(g_L_frac, LEFT)
mu_term.shift(SMALL_BUFF * UP / 2)
mu_brace = Brace(mu_term, UP)
mu_word = mu_brace.get_text("Air resistance")
for mob in equation, second_deriv_L_form, mu_term:
mob.set_color_by_tex("\\theta", BLUE)
self.play(
TransformFromCopy(x_eq[0], x_part),
Write(equation[:3]),
)
self.wait()
self.play(
Write(eq2),
TransformFromCopy(g_sin_theta, rhs)
)
self.wait()
#
self.show_acceleration_at_different_angles()
#
self.play(
FadeInFromDown(second_deriv_L_form),
Write(eq3),
second_deriv.next_to, eq3, UP,
a_eq.shift, SMALL_BUFF * LEFT,
eq2.shift, SMALL_BUFF * RIGHT,
rhs.shift, SMALL_BUFF * RIGHT,
)
self.wait()
self.wait()
self.play(
FadeOut(a_eq),
FadeOut(second_deriv),
FadeOut(eq3),
ReplacementTransform(
second_deriv_L_form.get_part_by_tex("L"),
g_L_frac.get_part_by_tex("L"),
),
ReplacementTransform(
equation.get_part_by_tex("-"),
g_L_frac.get_part_by_tex("-"),
),
ReplacementTransform(
equation.get_part_by_tex("g"),
g_L_frac.get_part_by_tex("g"),
),
Write(g_L_frac.get_part_by_tex("\\over")),
rhs[2:].next_to, g_L_frac, RIGHT, {"buff": SMALL_BUFF},
)
self.wait()
self.play(
GrowFromCenter(mu_term),
VGroup(eq2, second_deriv_L_form[1:]).next_to,
mu_term, LEFT,
)
self.play(
GrowFromCenter(mu_brace),
FadeInFromDown(mu_word),
)
def show_acceleration_at_different_angles(self):
to_fade = VGroup(
self.g_cos_label,
self.g_vect.perp,
)
new_comp_line_sytle = {
"stroke_width": 0.5,
"stroke_opacity": 0.25,
}
self.play(
FadeOut(self.x_eq),
to_fade.set_opacity, 0.25,
self.g_vect.component_lines.set_style,
new_comp_line_sytle
)
self.g_vect.component_lines.add_updater(
lambda m: m.set_style(**new_comp_line_sytle)
)
for mob in to_fade:
mob.add_updater(lambda m: m.set_opacity(0.25))
self.set_theta(0)
self.wait(2)
self.set_theta(89.9 * DEGREES, run_time=3)
self.wait(2)
self.set_theta(60 * DEGREES, run_time=2)
self.wait()
self.play(FadeInFrom(theta_decimal, UP))
self.set_theta(-60 * DEGREES, run_time=4)
self.set_theta(60 * DEGREES, run_time=4)
self.play(FadeOut(theta_decimal))
def show_angle_geometry(self):
g_vect = self.g_vect
vectors = VGroup(
g_vect, g_vect.tangent, g_vect.perp,
)
g_line = Line(
g_vect.get_start(),
g_vect.get_end(),
stroke_width=1,
stroke_color=WHITE,
)
arc = Arc(
start_angle=90 * DEGREES,
angle=self.pendulum.get_theta(),
radius=0.5,
arc_center=g_vect.get_end(),
)
q_mark = TexMobject("?")
q_mark.next_to(arc.get_center(), UL, SMALL_BUFF)
theta_label = TexMobject("\\theta")
theta_label.move_to(q_mark)
opposite = Line(
*g_vect.component_lines[0].get_start_and_end()
)
adjascent = g_line.copy()
opposite.set_stroke(BLUE, 5, opacity=1)
adjascent.set_stroke(YELLOW, 5)
vectors.save_state()
vectors.suspend_updating()
self.add(g_line, g_vect)
self.play(vectors.set_opacity, 0.3)
self.play(
ShowCreation(arc),
Write(q_mark)
)
self.play(ShowCreationThenFadeAround(q_mark))
self.wait()
self.play(ShowCreationThenFadeAround(
self.pendulum.theta_label
))
self.play(
TransformFromCopy(
self.pendulum.theta_label,
theta_label,
),
FadeOut(q_mark)
self.set_theta(
60 * DEGREES,
FadeIn(self.x_eq),
run_time=2,
)
self.wait()
self.play(ShowCreation(opposite))
self.play(ShowCreation(adjascent))
self.wait()
self.play(
FadeOut(opposite),
FadeOut(adjascent),
)
self.play(
Restore(vectors),
FadeOut(g_line),
FadeOut(arc),
FadeOut(theta_label),
)
vectors.resume_updating()
def ask_about_what_to_do(self):
g_vect = self.g_vect
@ -1349,96 +1525,6 @@ class AnalyzePendulumForce(MovingCameraScene):
pendulum.remove_updater(new_updater)
self.update_mobjects(0)
def show_arc_length(self):
pendulum = self.pendulum
to_fade = VGroup(
self.g_vect,
self.g_term,
self.g_vect.component_lines,
self.g_vect.tangent,
self.g_vect.perp,
self.g_sin_label,
self.g_cos_label,
)
angle = pendulum.get_theta()
height = pendulum.length
top = pendulum.get_fixed_point()
line = Line(UP, DOWN)
line.set_height(height)
line.move_to(top, UP)
arc = Arc(
start_angle=-90 * DEGREES,
angle=angle,
arc_center=top,
radius=height,
stroke_color=GREEN,
)
brace = Brace(Line(ORIGIN, 5 * UP), RIGHT)
brace.point = VectorizedPoint(brace.get_right())
brace.add(brace.point)
brace.set_height(angle)
brace.move_to(ORIGIN, DL)
brace.apply_complex_function(np.exp)
brace.scale(height)
brace.rotate(-90 * DEGREES)
brace.move_to(arc)
brace.shift(MED_SMALL_BUFF * normalize(
arc.point_from_proportion(0.5) - top
))
x_sym = TexMobject("x")
x_sym.set_color(GREEN)
x_sym.next_to(brace.point, DR, buff=SMALL_BUFF)
rhs = TexMobject("=", "L", "\\theta")
rhs.set_color_by_tex("\\theta", BLUE)
rhs.next_to(x_sym, RIGHT)
rhs.shift(0.7 * SMALL_BUFF * UP)
line_L = TexMobject("L")
line_L.next_to(
pendulum.rod.get_center(), UR, SMALL_BUFF,
)
if hasattr(self, "traced_arcs"):
self.play(FadeOut(self.traced_arcs))
self.play(FadeOut(to_fade))
self.play(
ShowCreation(arc),
Rotate(line, angle, about_point=top),
UpdateFromAlphaFunc(
line, lambda m, a: m.set_stroke(
width=2 * there_and_back(a)
)
),
GrowFromPoint(
brace, line.get_bottom(),
path_arc=angle
),
)
self.play(FadeInFrom(x_sym, UP))
self.wait()
# Show equation
line.set_stroke(BLUE, 5)
self.play(
ShowCreationThenFadeOut(line),
FadeInFromDown(line_L)
)
self.play(
TransformFromCopy(
line_L, rhs.get_part_by_tex("L")
),
Write(rhs.get_part_by_tex("="))
)
self.play(
TransformFromCopy(
pendulum.theta_label,
rhs.get_parts_by_tex("\\theta"),
)
)
self.add(rhs)
def show_angular_velocity(self):
pass

View file

@ -940,19 +940,26 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene):
"big_pendulum_config": {
"max_velocity_vector_length_to_length_ratio": 1,
},
"run_time": 25,
"initial_theta": 0,
"initial_theta_dot": 4,
"frame_shift_vect": TAU * RIGHT,
}
def setup(self):
MovingCameraScene.setup(self)
def construct(self):
self.initialize_plane_and_field()
self.add_flexible_state()
self.show_high_vector()
self.show_trajectory()
def initialize_plane_and_field(self):
self.initialize_plane()
self.add(self.plane)
self.initialize_vector_field()
self.add(self.vector_field)
self.add_flexible_state()
self.show_high_vector()
self.show_trajectory()
def add_flexible_state(self):
super().add_flexible_state()
@ -960,7 +967,10 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene):
plane = self.plane
state.to_edge(DOWN, buff=SMALL_BUFF),
start_point = plane.coords_to_point(0, 4)
start_point = plane.coords_to_point(
self.initial_theta,
self.initial_theta_dot,
)
dot = self.get_state_controlling_dot(state)
dot.move_to(start_point)
state.update()
@ -987,24 +997,14 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene):
def show_trajectory(self):
state = self.state
field = self.vector_field
frame = self.camera_frame
dot = self.dot
start_point = self.start_point
traj = VMobject()
traj.start_new_path(start_point)
dt = 0.01
total_time = 25
for x in range(int(total_time / dt)):
end = traj.points[-1]
dp_dt = field.func(end)
traj.add_smooth_curve_to(end + dp_dt * dt)
traj.set_stroke(WHITE, 2)
traj = self.get_trajectory(start_point, self.run_time)
self.add(traj, dot)
self.play(
anims = [
ShowCreation(
traj,
rate_func=linear,
@ -1012,15 +1012,32 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene):
UpdateFromFunc(
dot, lambda d: d.move_to(traj.points[-1])
),
ApplyMethod(
frame.shift, TAU * RIGHT,
rate_func=squish_rate_func(
smooth, 0, 0.3,
)
),
MaintainPositionRelativeTo(state.rect, frame),
run_time=total_time,
)
]
if get_norm(self.frame_shift_vect) > 0:
anims += [
ApplyMethod(
frame.shift, self.frame_shift_vect,
rate_func=squish_rate_func(
smooth, 0, 0.3,
)
),
MaintainPositionRelativeTo(state.rect, frame),
]
self.play(*anims, run_time=total_time)
def get_trajectory(self, start_point, time, dt=0.1, added_steps=1000):
field = self.vector_field
traj = VMobject()
traj.start_new_path(start_point)
for x in range(int(time / dt)):
last_point = traj.points[-1]
for y in range(added_steps):
dp_dt = field.func(last_point)
last_point += dp_dt * dt / added_steps
traj.add_smooth_curve_to(last_point)
traj.make_smooth()
traj.set_stroke(WHITE, 2)
return traj
class TweakMuInFormula(Scene):
@ -1146,6 +1163,216 @@ class TweakMuInVectorField(ShowPendulumPhaseFlow):
self.wait(self.flow_time)
class HighAmplitudePendulum(ShowHighVelocityCase):
CONFIG = {
"big_pendulum_config": {
"damping": 0.02,
},
"initial_theta": 175 * DEGREES,
"initial_theta_dot": 0,
"frame_shift_vect": 0 * RIGHT,
}
def construct(self):
self.initialize_plane_and_field()
self.add_flexible_state()
self.show_trajectory()
class SpectrumOfStartingStates(ShowHighVelocityCase):
CONFIG = {
"run_time": 15,
}
def construct(self):
self.initialize_plane_and_field()
self.vector_field.set_opacity(0.5)
self.show_many_trajectories()
def show_many_trajectories(self):
plane = self.plane
delta_x = 0.5
delta_y = 0.5
n = 20
start_points = [
plane.coords_to_point(x, y)
for x in np.linspace(PI - delta_x, PI + delta_x, n)
for y in np.linspace(-delta_y, delta_y, n)
]
start_points.sort(
key=lambda p: np.dot(p, UL)
)
time = self.run_time
# Count points
dots = VGroup(*[
Dot(sp, radius=0.025)
for sp in start_points
])
dots.set_color_by_gradient(PINK, BLUE, YELLOW)
words = TextMobject(
"Spectrum of\\\\", "initial conditions"
)
words.set_stroke(BLACK, 5, background=True)
words.next_to(dots, UP)
self.play(
# ShowIncreasingSubsets(dots, run_time=2),
LaggedStartMap(
FadeInFromLarge, dots,
lambda m: (m, 10),
run_time=2
),
FadeInFromDown(words),
)
self.wait()
trajs = VGroup()
for sp in start_points:
trajs.add(
self.get_trajectory(
sp, time,
added_steps=10,
)
)
for traj, dot in zip(trajs, dots):
traj.set_stroke(dot.get_color(), 1)
def update_dots(ds):
for d, t in zip(ds, trajs):
d.move_to(t.points[-1])
return ds
dots.add_updater(update_dots)
self.add(dots, trajs, words)
self.play(
ShowCreation(
trajs,
lag_ratio=0,
),
rate_func=linear,
run_time=time,
)
self.wait()
class AskAboutStability(ShowHighVelocityCase):
CONFIG = {
"initial_theta": 60 * DEGREES,
"initial_theta_dot": 1,
}
def construct(self):
self.initialize_plane_and_field()
self.add_flexible_state()
self.show_fixed_points()
self.label_fixed_points()
self.ask_about_stability()
self.show_nudges()
def show_fixed_points(self):
state1 = self.state
plane = self.plane
dot1 = self.dot
state2 = self.get_flexible_state_picture()
state2.to_corner(DR, buff=SMALL_BUFF)
dot2 = self.get_state_controlling_dot(state2)
dot2.set_color(BLUE)
fp1 = plane.coords_to_point(0, 0)
fp2 = plane.coords_to_point(PI, 0)
self.play(
dot1.move_to, fp1,
run_time=3,
)
self.wait()
self.play(FadeIn(state2))
self.play(
dot2.move_to, fp2,
path_arc=-30 * DEGREES,
run_time=2,
)
self.wait()
self.state1 = state1
self.state2 = state2
self.dot1 = dot1
self.dot2 = dot2
def label_fixed_points(self):
dots = VGroup(self.dot1, self.dot2)
label = TextMobject("Fixed points")
label.scale(1.5)
label.set_stroke(BLACK, 5, background=True)
label.next_to(dots, UP, buff=2)
label.shift(SMALL_BUFF * DOWN)
arrows = VGroup(*[
Arrow(
label.get_bottom(), dot.get_center(),
color=dot.get_color(),
)
for dot in dots
])
self.play(
self.vector_field.set_opacity, 0.5,
FadeInFromDown(label)
)
self.play(ShowCreation(arrows))
self.wait(2)
self.to_fade = VGroup(label, arrows)
def ask_about_stability(self):
question = TextMobject("Stable?")
question.scale(2)
question.shift(FRAME_WIDTH * RIGHT / 4)
question.to_edge(UP)
question.set_stroke(BLACK, 5, background=True)
self.play(Write(question))
self.play(FadeOut(self.to_fade))
def show_nudges(self):
dots = VGroup(self.dot1, self.dot2)
time = 20
self.play(*[
ApplyMethod(
dot.shift, 0.1 * UL,
rate_func=rush_from,
)
for dot in dots
])
trajs = VGroup()
for dot in dots:
traj = self.get_trajectory(
dot.get_center(),
time,
)
traj.set_stroke(dot.get_color(), 2)
trajs.add(traj)
def update_dots(ds):
for t, d in zip(trajs, ds):
d.move_to(t.points[-1])
dots.add_updater(update_dots)
self.add(trajs, dots)
self.play(
ShowCreation(trajs, lag_ratio=0),
rate_func=linear,
run_time=time
)
self.wait()
class TakeManyTinySteps(IntroduceVectorField):
CONFIG = {
"initial_theta": 60 * DEGREES,
@ -1153,14 +1380,16 @@ class TakeManyTinySteps(IntroduceVectorField):
}
def construct(self):
self.initialize_plane_and_field()
self.take_many_time_steps()
def initialize_plane_and_field(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
@ -1170,12 +1399,18 @@ class TakeManyTinySteps(IntroduceVectorField):
traj = always_redraw(
lambda: self.get_time_step_trajectory(
get_delta_t(), get_t()
get_delta_t(),
get_t(),
self.initial_theta,
self.initial_theta_dot,
)
)
vectors = always_redraw(
lambda: self.get_path_vectors(
get_delta_t(), get_t()
get_delta_t(),
get_t(),
self.initial_theta,
self.initial_theta_dot,
)
)
@ -1214,28 +1449,99 @@ class TakeManyTinySteps(IntroduceVectorField):
)
)
theta_t_label = TexMobject("\\theta(t)...\\text{ish}")
theta_t_label.scale(0.75)
theta_t_label.add_updater(lambda m: m.next_to(
vectors[-1].get_end(),
vectors[-1].get_vector(),
SMALL_BUFF,
))
self.add(traj, vectors, init_labels, labels)
time_tracker.set_value(0)
target_time = 10
self.play(
time_tracker.set_value, 10,
run_time=5,
rate_func=linear,
VFadeIn(theta_t_label),
ApplyMethod(
time_tracker.set_value, target_time,
run_time=5,
rate_func=linear,
)
)
self.wait()
t_label[-1].clear_updaters()
self.remove(theta_t_label)
target_delta_t = 0.01
self.play(
delta_t_tracker.set_value, 0.01,
delta_t_tracker.set_value, target_delta_t,
run_time=7,
)
self.wait()
traj.clear_updaters()
vectors.clear_updaters()
# Show steps
count_tracker = ValueTracker(0)
count = Integer()
count.scale(1.5)
count.to_edge(LEFT)
count.shift(UP)
count.add_updater(lambda c: c.set_value(
count_tracker.get_value()
))
count_label = TextMobject("steps")
count_label.scale(1.5)
count_label.add_updater(
lambda m: m.next_to(count, RIGHT).shift(SMALL_BUFF * DOWN)
)
scaled_vectors = vectors.copy()
scaled_vectors.clear_updaters()
for vector in scaled_vectors:
vector.scale(
1 / vector.get_length(),
about_point=vector.get_start()
)
vector.set_color(YELLOW)
def update_scaled_vectors(group):
group.set_opacity(0)
group[min(
int(count.get_value()),
len(group) - 1,
)].set_opacity(1)
scaled_vectors.add_updater(update_scaled_vectors)
self.add(count, count_label, scaled_vectors)
self.play(
# LaggedStartMap(
# ApplyFunction, vectors,
# lambda vector: (
# lambda v: v.scale(
# 1 / v.get_length()
# ),
# vector
# ),
# rate_func=there_and_back,
# ),
ApplyMethod(
count_tracker.set_value,
int(target_time / target_delta_t),
rate_func=linear,
),
run_time=5,
)
self.play(FadeOut(scaled_vectors))
self.wait()
#
def get_time_step_points(self, delta_t, total_time):
def get_time_step_points(self, delta_t, total_time, theta_0, theta_dot_0):
plane = self.plane
field = self.vector_field
curr_point = plane.coords_to_point(
self.initial_theta,
self.initial_theta_dot,
theta_0,
theta_dot_0,
)
points = [curr_point]
t = 0
@ -1246,17 +1552,21 @@ class TakeManyTinySteps(IntroduceVectorField):
t += delta_t
return points
def get_time_step_trajectory(self, delta_t, total_time):
def get_time_step_trajectory(self, delta_t, total_time, theta_0, theta_dot_0):
traj = VMobject()
traj.set_points_as_corners(
self.get_time_step_points(delta_t, total_time)
self.get_time_step_points(
delta_t, total_time,
theta_0, theta_dot_0,
)
)
traj.set_stroke(WHITE, 2)
return traj
def get_path_vectors(self, delta_t, total_time):
def get_path_vectors(self, delta_t, total_time, theta_0, theta_dot_0):
corners = self.get_time_step_points(
delta_t, total_time
delta_t, total_time,
theta_0, theta_dot_0,
)
result = VGroup()
for a1, a2 in zip(corners, corners[1:]):
@ -1268,3 +1578,51 @@ class TakeManyTinySteps(IntroduceVectorField):
)
result.add(vector)
return result
class ManyStepsFromDifferentStartingPoints(TakeManyTinySteps):
CONFIG = {
"initial_thetas": np.linspace(0.1, PI - 0.1, 10),
"initial_theta_dot": 0,
}
def construct(self):
self.initialize_plane_and_field()
self.take_many_time_steps()
def take_many_time_steps(self):
delta_t_tracker = ValueTracker(0.2)
get_delta_t = delta_t_tracker.get_value
time_tracker = ValueTracker(10)
get_t = time_tracker.get_value
# traj = always_redraw(
# lambda: VGroup(*[
# self.get_time_step_trajectory(
# get_delta_t(),
# get_t(),
# theta,
# self.initial_theta_dot,
# )
# for theta in self.initial_thetas
# ])
# )
vectors = always_redraw(
lambda: VGroup(*[
self.get_path_vectors(
get_delta_t(),
get_t(),
theta,
self.initial_theta_dot,
)
for theta in self.initial_thetas
])
)
self.add(vectors)
time_tracker.set_value(0)
self.play(
time_tracker.set_value, 5,
run_time=5,
rate_func=linear,
)

View file

@ -108,6 +108,29 @@ class FormulasAreLies(PiCreatureScene):
# pass
class SoWhatIsThetaThen(TeacherStudentsScene):
def construct(self):
ode = get_ode()
ode.to_corner(UL)
self.add(ode)
self.student_says(
"Okay, but then\\\\"
"what \\emph{is} $\\theta(t)$?"
)
self.wait()
self.play(self.teacher.change, "happy")
self.wait(2)
self.teacher_says(
"First, you must appreciate\\\\"
"a deep truth...",
added_anims=[self.get_student_changes(
*3 * ["confused"]
)]
)
self.wait(4)
class ProveTeacherWrong(TeacherStudentsScene):
def construct(self):
tex_config = {
@ -247,9 +270,7 @@ class HungerForExactness(TeacherStudentsScene):
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}"
@ -401,19 +422,37 @@ class HungerForExactness(TeacherStudentsScene):
mystery.scale(2)
mystery.to_edge(UP)
mystery.set_stroke(width=0, background=True)
mystery_boundary = AnimatedBoundary(
mystery, stroke_width=1
)
self.play(
FadeInFromDown(mystery),
self.teacher.change, "pondering"
)
self.add(
AnimatedBoundary(mystery, stroke_width=1),
mystery,
)
self.add(mystery_boundary, mystery)
self.change_all_student_modes("sad")
self.look_at(mystery)
self.wait(5)
# Define
self.student_says(
"Let $\\text{P}(\\mu, g, L; t)$ be a\\\\"
"function satisfying this ODE.",
student_index=0,
target_mode="speaking",
added_anims=[
FadeOut(mystery),
FadeOut(mystery_boundary),
ode.to_corner, UR
]
)
self.change_student_modes(
"hooray", "sassy", "sassy",
look_at_arg=students[0].eyes.get_corner(UR),
)
self.wait(2)
class ItGetsWorse(TeacherStudentsScene):
def construct(self):
@ -421,4 +460,4 @@ class ItGetsWorse(TeacherStudentsScene):
self.change_student_modes(
"hesitant", "pleading", "erm"
)
self.wait(2)
self.wait(5)

View file

@ -20,8 +20,8 @@ def get_ode():
tex_config = {
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": YELLOW,
"{\\ddot\\theta}": RED,
"{\\dot\\theta}": RED,
"{\\ddot\\theta}": YELLOW,
"{t}": WHITE,
"{\\mu}": WHITE,
}
@ -37,7 +37,7 @@ def get_ode():
def get_period_formula():
return TexMobject(
"\\sqrt{\\,", "2\\pi", "L", "/", "g", "}",
"2\\pi", "\\sqrt{\\,", "L", "/", "g", "}",
tex_to_color_map={
"L": BLUE,
"g": YELLOW,

View file

@ -661,8 +661,8 @@ class DefineODE(Scene):
tex_config = {
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": YELLOW,
"{\\ddot\\theta}": RED,
"{\\dot\\theta}": RED,
"{\\ddot\\theta}": YELLOW,
},
"height": 0.5,
}
@ -698,7 +698,7 @@ class DefineODE(Scene):
def get_tangent_line(curve, alpha):
line = Line(
ORIGIN, 1.5 * RIGHT,
color=YELLOW,
color=RED,
stroke_width=1.5,
)
da = 0.0001
@ -722,7 +722,7 @@ class DefineODE(Scene):
get_point(t + a)
for a in np.linspace(-0.5, 0.5, 11)
])
curve.set_stroke(RED, 1)
curve.set_stroke(YELLOW, 1)
return curve
v_line = always_redraw(get_v_line)
@ -737,8 +737,8 @@ class DefineODE(Scene):
words = VGroup(
TextMobject("= Height").set_color(BLUE),
TextMobject("= Slope").set_color(YELLOW),
TextMobject("= ``Curvature''").set_color(RED),
TextMobject("= Slope").set_color(RED),
TextMobject("= ``Curvature''").set_color(YELLOW),
)
words.scale(0.75)
for word, sym in zip(words, thetas):
@ -872,7 +872,7 @@ class DefineODE(Scene):
self.wait()
self.play(FocusOn(second_deriv))
self.play(
Indicate(second_deriv, color=RED),
Indicate(second_deriv, color=YELLOW),
)
self.wait()
@ -1001,8 +1001,8 @@ class SecondOrderEquationExample(DefineODE):
"-", "\\omega", "{x}(t)",
tex_to_color_map={
"{x}": BLUE,
"{\\dot x}": YELLOW,
"{\\ddot x}": RED,
"{\\dot x}": RED,
"{\\ddot x}": YELLOW,
}
)
equation.next_to(de_word, DOWN)
@ -1031,8 +1031,8 @@ class SecondOrderEquationExample(DefineODE):
tex_config = {
"tex_to_color_map": {
"{x}": BLUE,
"{\\dot x}": YELLOW,
"{\\ddot x}": RED,
"{\\dot x}": RED,
"{\\ddot x}": YELLOW,
},
"height": 0.5,
}
@ -1068,7 +1068,7 @@ class SecondOrderEquationExample(DefineODE):
def get_tangent_line(curve, alpha):
line = Line(
ORIGIN, 1.5 * RIGHT,
color=YELLOW,
color=RED,
stroke_width=1.5,
)
da = 0.0001
@ -1092,7 +1092,7 @@ class SecondOrderEquationExample(DefineODE):
get_point(t + a)
for a in np.linspace(-0.5, 0.5, 11)
])
curve.set_stroke(RED, 1)
curve.set_stroke(YELLOW, 1)
return curve
v_line = always_redraw(get_v_line)
@ -1107,8 +1107,8 @@ class SecondOrderEquationExample(DefineODE):
words = VGroup(
TextMobject("= Height").set_color(BLUE),
TextMobject("= Slope").set_color(YELLOW),
TextMobject("= ``Curvature''").set_color(RED),
TextMobject("= Slope").set_color(RED),
TextMobject("= ``Curvature''").set_color(YELLOW),
)
words.scale(0.75)
for word, sym in zip(words, xs):
@ -1511,13 +1511,7 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
bodies = self.bodies = VGroup()
velocity_vectors = VGroup()
centers = [
np.dot(
4 * (np.random.random(3) - 0.5),
[RIGHT, UP, OUT]
)
for x in range(len(masses))
]
centers = self.get_initial_positions()
for mass, color, center in zip(masses, colors, centers):
body = self.get_sphere(
@ -1529,31 +1523,21 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
)
body.set_opacity(0.75)
body.mass = mass
body.set_width(0.15 * np.sqrt(mass))
body.radius = 0.08 * np.sqrt(mass)
body.set_width(2 * body.radius)
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)
body.velocity = self.get_initial_velocity(
center, centers, mass
)
vect = self.get_velocity_vector_mob(body)
bodies.add(body)
velocity_vectors.add(vect)
self.add(body)
# self.add(vect)
total_mass = np.sum([body.mass for body in bodies])
center_of_mass = reduce(op.add, [
body.mass * body.get_center() / total_mass
@ -1567,6 +1551,26 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
body.shift(-center_of_mass)
body.velocity -= average_momentum
def get_initial_positions(self):
return [
np.dot(
4 * (np.random.random(3) - 0.5),
[RIGHT, UP, OUT]
)
for x in range(len(self.masses))
]
def get_initial_velocity(self, center, centers, mass):
to_others = [
center - center2
for center2 in centers
]
velocity = 0.2 * mass * normalize(np.cross(*filter(
lambda diff: get_norm(diff) > 0,
to_others
)))
return velocity
def add_trajectories(self):
def update_trajectory(traj, dt):
new_point = traj.body.point
@ -1582,8 +1586,11 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
self.add(traj, body)
def let_play(self):
bodies = self.bodies
bodies.add_updater(self.update_bodies)
# Break it up to see partial files as
# it's rendered
self.add(bodies)
for x in range(int(self.play_time)):
self.wait()
@ -1607,23 +1614,25 @@ class ThreeBodiesInSpace(SpecialThreeDScene):
# )
return always_redraw(draw_vector)
def update_body(self, body, dt):
def update_bodies(self, bodies, dt):
G = self.G
acceleration = np.zeros(3)
for body2 in self.bodies:
if body2 is body:
continue
diff = body2.point - body.point
m2 = body2.mass
R = get_norm(diff)
acceleration += G * m2 * diff / (R**3)
num_mid_steps = 100
num_mid_steps = 1000
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
for body in bodies:
acceleration = np.zeros(3)
for body2 in bodies:
if body2 is body:
continue
diff = body2.point - body.point
m2 = body2.mass
R = get_norm(diff)
acceleration += G * m2 * diff / (R**3)
body.point += body.velocity * dt / num_mid_steps
body.velocity += acceleration * dt / num_mid_steps
for body in bodies:
body.move_to(body.point)
return bodies
class AltThreeBodiesInSpace(ThreeBodiesInSpace):
@ -1636,16 +1645,96 @@ class AltThreeBodiesInSpace(ThreeBodiesInSpace):
class TwoBodiesInSpace(ThreeBodiesInSpace):
CONFIG = {
"colors": [GREY, BLUE],
"masses": [1, 6],
"play_time": 5,
"masses": [6, 36],
"play_time": 60,
}
def construct(self):
self.add_axes()
self.add_bodies()
self.add_trajectories()
self.add_velocity_vectors()
self.add_force_vectors()
self.let_play()
def add_bodies(self):
super().add_bodies()
for body in self.bodies:
body.point = 3 * normalize(body.get_center())
# body.point += 2 * IN
# body.velocity += (4 / 60) * OUT
body.move_to(body.point)
def get_initial_positions(self):
return [
np.dot(
6 * (np.random.random(3) - 0.5),
[RIGHT, UP, ORIGIN]
)
for x in range(len(self.masses))
]
def get_initial_velocity(self, center, centers, mass):
return 0.75 * normalize(np.cross(center, OUT))
def add_velocity_vectors(self):
vectors = VGroup(*[
self.get_velocity_vector(body)
for body in self.bodies
])
self.velocity_vectors = vectors
self.add(vectors)
def get_velocity_vector(self, body):
def create_vector(b):
v = Vector(
b.velocity,
color=RED,
max_stroke_width_to_length_ratio=3,
)
v.set_stroke(width=3)
v.shift(
b.point + b.radius * normalize(b.velocity) -
v.get_start(),
)
v.set_shade_in_3d(True)
return v
return always_redraw(lambda: create_vector(body))
def add_force_vectors(self):
vectors = VGroup(*[
self.get_force_vector(b1, b2)
for (b1, b2) in (self.bodies, self.bodies[::-1])
])
self.force_vectors = vectors
self.add(vectors)
def get_force_vector(self, body1, body2):
def create_vector(b1, b2):
r = b2.point - b1.point
F = r / (get_norm(r)**3)
v = Vector(
4 * F,
color=YELLOW,
max_stroke_width_to_length_ratio=3,
)
v.set_stroke(width=3)
v.shift(
b1.point + b1.radius * normalize(F) -
v.get_start(),
)
v.set_shade_in_3d(True)
return v
return always_redraw(lambda: create_vector(body1, body2))
class TwoBodiesWithZPart(TwoBodiesInSpace):
def add_bodies(self):
super().add_bodies()
for body in self.bodies:
body.point += 3 * IN
body.velocity += (6 / 60) * OUT
class DefineODECopy(DefineODE):
pass

View file

@ -97,6 +97,289 @@ class StrogatzQuote(Scene):
return quote
class WriteInRadians(Scene):
def construct(self):
words = TextMobject("In radians")
words.set_color(YELLOW)
square = SurroundingRectangle(TexMobject("\\theta"))
square.next_to(words, UP)
self.play(ShowCreation(square))
self.play(Write(words), FadeOut(square))
self.wait()
class XEqLThetaToCorner(Scene):
def construct(self):
equation = TexMobject(
"x = L\\theta",
tex_to_color_map={
"x": GREEN,
"\\theta": BLUE,
}
)
equation.move_to(DOWN + 3 * RIGHT)
self.add(equation)
self.play(equation.to_corner, DL, {"buff": LARGE_BUFF})
self.wait()
class ComingUp(Scene):
CONFIG = {
"camera_config": {"background_color": DARKER_GREY}
}
def construct(self):
frame = ScreenRectangle(
stroke_width=0,
fill_color=BLACK,
fill_opacity=1,
height=6
)
title = TextMobject("Coming up")
title.scale(1.5)
title.to_edge(UP)
frame.next_to(title, DOWN)
animated_frame = AnimatedBoundary(frame)
self.add(frame, title, animated_frame)
self.wait(10)
class InputLabel(Scene):
def construct(self):
label = TextMobject("Input")
label.scale(1.25)
arrow = Vector(UP)
arrow.next_to(label, UP)
self.play(
FadeInFrom(label, UP),
GrowArrow(arrow)
)
self.wait()
class ReallyHardToSolve(Scene):
def construct(self):
words = TextMobject(
"They're", "really\\\\",
"freaking", "hard\\\\",
"to", "solve\\\\",
)
words.set_height(6)
self.wait()
for word in words:
wait_time = 0.05 * len(word)
self.add(word)
self.wait(wait_time)
self.wait()
class ReasonForSolution(Scene):
def construct(self):
# Words
eq_word = TextMobject("Differential\\\\Equation")
s_word = TextMobject("Solution")
u_word = TextMobject("Understanding")
c_word = TextMobject("Computation")
cu_group = VGroup(u_word, c_word)
cu_group.arrange(DOWN, buff=2)
group = VGroup(eq_word, s_word, cu_group)
group.arrange(RIGHT, buff=2)
words = VGroup(eq_word, s_word, u_word, c_word)
# Arrows
arrows = VGroup(
Arrow(eq_word.get_right(), s_word.get_left()),
Arrow(s_word.get_right(), u_word.get_left()),
Arrow(s_word.get_right(), c_word.get_left()),
)
arrows.set_color(LIGHT_GREY)
new_arrows = VGroup(
Arrow(
eq_word.get_corner(UR),
u_word.get_left(),
path_arc=-60 * DEGREES,
),
Arrow(
eq_word.get_corner(DR),
c_word.get_left(),
path_arc=60 * DEGREES,
),
)
new_arrows.set_color(BLUE)
# Define first examples
t2c = {
"{x}": BLUE,
"{\\dot x}": RED,
}
equation = TexMobject(
"{\\dot x}(t) = k {x}(t)",
tex_to_color_map=t2c,
)
equation.next_to(eq_word, DOWN)
solution = TexMobject(
"{x}(t) = x_0 e^{kt}",
tex_to_color_map=t2c,
)
solution.next_to(s_word, DOWN, MED_LARGE_BUFF)
equation.align_to(solution, DOWN)
axes = Axes(
x_min=-1,
x_max=5.5,
y_min=-1,
y_max=4.5,
y_axis_config={"unit_size": 0.5}
)
axes.set_stroke(width=2)
graph_line = axes.get_graph(
lambda x: np.exp(0.4 * x)
)
graph_line.set_stroke(width=2)
graph = VGroup(axes, graph_line)
graph.scale(0.5)
graph.next_to(u_word, UP)
computation = TexMobject(
# "\\displaystyle "
"e^x = \\sum_{n=0}^\\infty "
"\\frac{x^n}{n!}"
)
computation.next_to(c_word, DOWN)
first_examples = VGroup(
equation, solution, graph, computation
)
# Second example
ode = get_ode()
ode.scale(0.75)
second_examples = VGroup(
ode,
TexMobject("???").set_color(LIGHT_GREY),
ScreenRectangle(
height=2,
stroke_width=1,
),
)
for fe, se in zip(first_examples, second_examples):
se.move_to(fe, DOWN)
ode.shift(2 * SMALL_BUFF * DOWN)
ode.add_to_back(BackgroundRectangle(ode[-4:]))
self.add(eq_word)
self.add(equation)
self.play(
FadeInFrom(s_word, LEFT),
GrowArrow(arrows[0]),
TransformFromCopy(equation, solution)
)
self.wait()
self.play(
FadeInFrom(c_word, UL),
GrowArrow(arrows[2]),
FadeInFrom(computation, UP)
)
self.wait()
self.play(
FadeInFrom(u_word, DL),
GrowArrow(arrows[1]),
FadeInFromDown(graph)
)
self.wait(2)
self.play(
FadeOut(first_examples),
FadeIn(second_examples[:2])
)
self.wait()
self.play(
arrows.fade, 0.75,
s_word.fade, 0.75,
second_examples[1].fade, 0.75,
ShowCreation(new_arrows[0]),
FadeIn(second_examples[2])
)
self.play(
ShowCreation(new_arrows[1]),
Animation(second_examples),
)
self.wait()
class WritePhaseSpace(Scene):
def construct(self):
word = TextMobject("Phase space")
word.scale(2)
word.shift(FRAME_WIDTH * LEFT / 4)
word.to_edge(UP)
word.add_background_rectangle()
lines = VGroup(*[
Line(v, 1.3 * v)
for v in compass_directions(50)
])
lines.replace(word, stretch=True)
lines.scale(1.5)
lines.set_stroke(YELLOW)
lines.shuffle()
self.add(word)
self.play(
ShowPassingFlashWithThinningStrokeWidth(
lines,
lag_ratio=0.002,
run_time=1.5,
time_width=0.9,
n_segments=5,
)
)
self.wait()
class GleickQuote(Scene):
def construct(self):
quote = TextMobject(
"``[Phase space is] one of the most\\\\",
"powerful inventions", "of modern science.''\\\\",
)
quote.power_part = quote.get_part_by_tex("power")
book = ImageMobject("ChaosBookCover")
book.set_height(5)
book.next_to(ORIGIN, LEFT)
book.to_edge(DOWN)
gleick = ImageMobject("JamesGleick")
gleick.set_height(5)
gleick.next_to(ORIGIN, RIGHT)
gleick.to_edge(DOWN)
quote.to_edge(UP)
self.play(
FadeInFrom(book, RIGHT),
FadeInFrom(gleick, LEFT),
)
self.wait()
self.play(Write(quote))
self.play(Write(
quote.power_part.copy().set_color(BLUE),
run_time=1
))
self.wait()
class WritePhaseFlow(Scene):
def construct(self):
words = TextMobject("Phase flow")
words.scale(2)
words.shift(FRAME_WIDTH * LEFT / 4)
words.to_edge(UP)
words.add_background_rectangle()
self.play(Write(words))
self.wait()
class ShowSineValues(Scene):
def construct(self):
angle_tracker = ValueTracker(60 * DEGREES)