mirror of
https://github.com/3b1b/manim.git
synced 2025-09-19 04:41:56 +00:00
Pile of ODE scenes
This commit is contained in:
parent
e8fe67650a
commit
9d566de8d1
7 changed files with 1178 additions and 304 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue