Merge pull request #487 from 3b1b/ode

Ode
This commit is contained in:
Grant Sanderson 2019-04-04 08:55:38 -07:00 committed by GitHub
commit 8be321d104
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 4879 additions and 520 deletions

View file

@ -6,8 +6,11 @@ from active_projects.ode.part1.wordy_scenes import *
OUTPUT_DIRECTORY = "ode/part1"
ALL_SCENE_CLASSES = [
WhenChangeIsEasier,
VectorFieldTest,
IntroducePendulum,
MultiplePendulumsOverlayed,
PeriodFormula,
FormulasAreLies,
MediumAnglePendulum,
MediumHighAnglePendulum,
@ -18,21 +21,57 @@ ALL_SCENE_CLASSES = [
VeryLowAnglePendulum,
FormulasAreLies,
TourOfDifferentialEquations,
WherePendulumLeads,
LongDoublePendulum,
# FollowThisThread,
StrogatzQuote,
ShowHorizontalDashedLine,
RabbitFoxPopulations,
RabbitFoxEquation,
# Something...
ShowSimpleTrajectory,
SimpleProjectileEquation,
SimpleProjectileEquationVGraphFreedom,
ShowGravityAcceleration,
UniversalGravityLawSymbols,
ExampleTypicalODE,
AnalyzePendulumForce,
ShowSineValues,
BuildUpEquation,
AirResistanceBrace,
ShowDerivativeVideo,
SubtleAirCurrents,
SimpleDampenedPendulum,
DefineODE,
SecondOrderEquationExample,
ODEvsPDEinFrames,
ProveTeacherWrong,
SetAsideSeekingSolution,
ReferencePiCollisionStateSpaces,
#
WriteInRadians,
XEqLThetaToCorner,
ComingUp,
InputLabel,
SoWhatIsThetaThen,
ReallyHardToSolve,
ReasonForSolution,
PhysicistPhaseSpace,
GleickQuote,
SpectrumOfStartingStates,
WritePhaseFlow,
AskAboutStability,
LoveExample,
PassageOfTime,
LovePhaseSpace,
ComparePhysicsToLove,
FramesComparingPhysicsToLove,
SetupToTakingManyTinySteps,
ShowClutterPrevention,
# VisualizeHeightSlopeCurvature,
VisualizeStates,
ReferencePiCollisionStateSpaces,
IntroduceVectorField,
XComponentArrows,
BreakingSecondOrderIntoTwoFirstOrder,
ShowPendulumPhaseFlow,
ShowHighVelocityCase,
@ -42,10 +81,27 @@ ALL_SCENE_CLASSES = [
LorenzVectorField,
ThreeBodiesInSpace,
AltThreeBodiesInSpace,
TwoBodiesInSpace,
TwoBodiesWithZPart,
ThreeBodyTitle,
ThreeBodySymbols,
#
HighAmplitudePendulum,
WritePhaseSpace,
#
AskAboutActuallySolving,
WriteODESolvingCode,
TakeManyTinySteps,
ManyStepsFromDifferentStartingPoints,
InaccurateComputation,
HungerForExactness,
ShowRect,
ShowSquare,
JumpToThisPoint,
ThreeBodyEquation,
ItGetsWorse,
ChaosTitle,
RevisitQuote,
EndScreen,
Thumbnail,
]

View file

@ -0,0 +1,19 @@
from active_projects.ode.part2.staging import *
from active_projects.ode.part2.fourier_series import *
from active_projects.ode.part2.heat_equation import *
OUTPUT_DIRECTORY = "ode/part2"
ALL_SCENE_CLASSES = [
# Tests
FourierOfPiSymbol,
FourierOfPiSymbol5,
FourierOfTrebleClef,
# CirclesDrawingWave,
# Scenes for video
ExplainCircleAnimations,
FourierSeriesIntro,
FourierSeriesIntroBackground4,
FourierSeriesIntroBackground8,
FourierSeriesIntroBackground12,
FourierSeriesIntroBackground20,
]

View file

@ -287,6 +287,9 @@ class ThetaVsTAxes(Axes):
y_axis.label = theta_label
y_axis.add(theta_label)
self.y_axis_label = theta_label
self.x_axis_label = t_label
x_axis.add_numbers()
y_axis.add(self.get_y_axis_coordinates(y_axis))
@ -357,6 +360,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
"length": 3,
"top_point": 4 * RIGHT,
"weight_diameter": 0.35,
"gravity": 20,
},
"theta_vs_t_axes_config": {
"y_max": PI / 4,
@ -366,6 +370,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
"unit_size": 2,
"tip_length": 0.3,
},
"x_max": 12,
"number_line_config": {
"stroke_width": 2,
}
@ -378,12 +383,13 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
def construct(self):
self.add_pendulum()
self.label_pi_creatures()
# self.label_pi_creatures()
self.label_pendulum()
self.add_graph()
self.label_function()
self.show_graph_period()
self.show_length_and_gravity()
self.tweak_length_and_gravity()
# self.tweak_length_and_gravity()
def create_pi_creatures(self):
randy = Randolph(color=BLUE_C)
@ -437,7 +443,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
GrowArrow(morty_label.arrow),
morty.change, "raise_right_hand",
)
self.wait()
self.wait(2)
def label_pendulum(self):
pendulum = self.pendulum
@ -446,18 +452,18 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
rect = SurroundingRectangle(label, buff=0.5 * SMALL_BUFF)
rect.add_updater(lambda r: r.move_to(label))
self.add(rect)
for pi in randy, morty:
pi.add_updater(
lambda m: m.look_at(pendulum.weight)
)
self.play(randy.change, "pondering")
self.play(morty.change, "pondering")
self.wait(3)
randy.clear_updaters()
morty.clear_updaters()
self.play(
ShowCreationThenFadeOut(rect),
ShowCreationThenDestruction(
label.copy().set_style(
fill_opacity=0,
stroke_color=PINK,
stroke_width=2,
)
),
randy.change, "pondering",
morty.change, "pondering",
)
self.wait()
@ -467,7 +473,10 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
axes.to_corner(UL)
self.play(
Restore(self.camera_frame),
Restore(
self.camera_frame,
rate_func=squish_rate_func(smooth, 0, 0.9),
),
DrawBorderThenFill(
axes,
rate_func=squish_rate_func(smooth, 0.5, 1),
@ -481,10 +490,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
),
run_time=3,
)
self.wait(2)
self.graph = axes.get_live_drawn_graph(self.pendulum)
self.wait(1.5)
self.graph = axes.get_live_drawn_graph(self.pendulum)
self.add(self.graph)
def label_function(self):
hm_word = TextMobject("Simple harmonic motion")
hm_word.scale(1.25)
hm_word.to_edge(UP)
formula = TexMobject(
"=\\theta_0 \\cos(\\sqrt{g / L} t)"
)
formula.next_to(
self.axes.y_axis_label, RIGHT, SMALL_BUFF
)
formula.set_stroke(width=0, background=True)
self.play(FadeInFrom(hm_word, DOWN))
self.wait()
self.play(
Write(formula),
hm_word.to_corner, UR
)
self.wait(4)
def show_graph_period(self):
@ -503,13 +532,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
line.shift(SMALL_BUFF * RIGHT)
brace = Brace(line, UP, buff=SMALL_BUFF)
brace.add_to_back(brace.copy().set_style(BLACK, 10))
formula = TexMobject(
"\\sqrt{\\,", "2\\pi", "L", "/", "g", "}",
tex_to_color_map={
"L": BLUE,
"g": YELLOW,
}
)
formula = get_period_formula()
formula.next_to(brace, UP, SMALL_BUFF)
self.period_formula = formula
@ -537,29 +560,22 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
self.pendulum,
length_multiple=0.5 / 9.8,
)
down_vectors = self.get_down_vectors()
down_vectors.set_color(YELLOW)
down_vectors.set_opacity(0.5)
self.play(ShowCreationThenDestructionAround(L))
dot = Dot(fill_opacity=0.25)
dot.move_to(L)
self.play(
ShowCreationThenDestructionAround(L),
ShowCreation(new_rod),
dot.move_to, new_rod,
dot.fade, 1,
)
self.remove(dot)
self.play(FadeOut(new_rod))
self.wait()
self.play(ShowCreationThenDestructionAround(g))
dot.move_to(g)
dot.set_fill(opacity=0.5)
self.play(
ShowCreationThenDestructionAround(g),
GrowArrow(g_vect),
dot.move_to, g_vect,
dot.fade, 1,
)
self.remove(dot)
self.wait(2)
self.play(self.get_down_vectors_animation(down_vectors))
self.wait(6)
self.gravity_vector = g_vect
@ -593,15 +609,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
# new_pendulum_config["initial_theta"] = pendulum.get_theta()
new_pendulum = Pendulum(**new_pendulum_config)
down_vectors = VGroup(*[
Vector(0.5 * DOWN)
for x in range(10 * 150)
])
down_vectors.arrange_in_grid(10, 150, buff=MED_SMALL_BUFF)
down_vectors.set_color_by_gradient(BLUE, RED)
# for vect in down_vectors:
# vect.shift(0.1 * np.random.random(3))
down_vectors.to_edge(RIGHT)
down_vectors = self.get_down_vectors()
self.play(randy.change, "happy")
self.play(
@ -624,10 +632,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
g_vect.scale(2)
self.play(
FadeOut(graph2),
LaggedStart(*[
GrowArrow(v, rate_func=there_and_back)
for v in down_vectors
], lag_ratio=0.0005, run_time=2, remover=True)
self.get_down_vectors_animation(down_vectors)
)
self.play(
FadeIn(graph3),
@ -635,6 +640,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
)
self.wait(6)
#
def get_down_vectors(self):
down_vectors = VGroup(*[
Vector(0.5 * DOWN)
for x in range(10 * 150)
])
down_vectors.arrange_in_grid(10, 150, buff=MED_SMALL_BUFF)
down_vectors.set_color_by_gradient(BLUE, RED)
# for vect in down_vectors:
# vect.shift(0.1 * np.random.random(3))
down_vectors.to_edge(RIGHT)
return down_vectors
def get_down_vectors_animation(self, down_vectors):
return LaggedStart(
*[
GrowArrow(v, rate_func=there_and_back)
for v in down_vectors
],
lag_ratio=0.0005,
run_time=2,
remover=True
)
class MultiplePendulumsOverlayed(Scene):
CONFIG = {
@ -868,6 +897,95 @@ class VeryLowAnglePendulum(LowAnglePendulum):
}
class WherePendulumLeads(PiCreatureScene):
def construct(self):
pendulum = Pendulum(
top_point=UP,
length=3,
gravity=20,
)
pendulum.start_swinging()
l_title = TextMobject("Linearization")
l_title.scale(1.5)
l_title.to_corner(UL)
c_title = TextMobject("Chaos")
c_title.scale(1.5)
c_title.move_to(l_title)
c_title.move_to(
c_title.get_center() * np.array([-1, 1, 1])
)
get_theta = pendulum.get_theta
spring = always_redraw(
lambda: ParametricFunction(
lambda t: np.array([
np.cos(TAU * t) + (1.4 + get_theta()) * t,
np.sin(TAU * t) - 0.5,
0,
]),
t_min=-0.5,
t_max=7,
color=GREY,
sheen_factor=1,
sheen_direction=UL,
).scale(0.2).to_edge(LEFT, buff=0)
)
spring_rect = SurroundingRectangle(
spring, buff=MED_LARGE_BUFF,
stroke_width=0,
fill_color=BLACK,
fill_opacity=0,
)
weight = Dot(radius=0.25)
weight.add_updater(lambda m: m.move_to(
spring.points[-1]
))
weight.set_color(BLUE)
weight.set_sheen(1, UL)
spring_system = VGroup(spring, weight)
linear_formula = TexMobject(
"\\frac{d \\vec{\\textbf{x}}}{dt}="
"A\\vec{\\textbf{x}}"
)
linear_formula.next_to(spring, UP, LARGE_BUFF)
linear_formula.match_x(l_title)
randy = self.pi_creature
randy.set_height(2)
randy.center()
randy.to_edge(DOWN)
randy.shift(3 * LEFT)
q_marks = TexMobject("???")
q_marks.next_to(randy, UP)
self.add(pendulum, randy)
self.play(
randy.change, "pondering", pendulum,
FadeInFromDown(q_marks, lag_ratio=0.3)
)
self.play(randy.look_at, pendulum)
self.wait(5)
self.play(
Animation(VectorizedPoint(pendulum.get_top())),
FadeOutAndShift(q_marks, UP, lag_ratio=0.3),
)
self.add(spring_system)
self.play(
FadeOut(spring_rect),
FadeInFrom(linear_formula, UP),
FadeInFromDown(l_title),
)
self.play(FadeInFromDown(c_title))
self.wait(8)
class LongDoublePendulum(ExternallyAnimatedScene):
pass
class AnalyzePendulumForce(MovingCameraScene):
CONFIG = {
"pendulum_config": {
@ -885,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)
@ -910,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
@ -927,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]:
@ -946,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(
@ -954,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
@ -983,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
@ -1037,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(
@ -1070,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
@ -1215,103 +1508,22 @@ class AnalyzePendulumForce(MovingCameraScene):
self.play(FocusOn(pendulum.theta_label))
self.play(Indicate(pendulum.theta_label))
old_updaters = pendulum.get_updaters()
pendulum.clear_updaters(recursive=False)
pendulum.start_swinging()
pendulum_copy = pendulum.deepcopy()
pendulum_copy.clear_updaters()
pendulum_copy.fade(1)
pendulum_copy.start_swinging()
def new_updater(p):
p.set_theta(pendulum_copy.get_theta())
pendulum.add_updater(new_updater)
self.add(pendulum_copy)
self.wait(5)
pendulum.clear_updaters()
for updater in old_updaters:
pendulum.add_updater(updater)
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)
pendulum_copy.end_swinging()
self.remove(pendulum_copy)
pendulum.remove_updater(new_updater)
self.update_mobjects(0)
def show_angular_velocity(self):
pass
@ -1664,6 +1876,18 @@ class BuildUpEquation(Scene):
self.wait()
class SimpleDampenedPendulum(Scene):
def construct(self):
pendulum = Pendulum(
top_point=ORIGIN,
initial_theta=150 * DEGREES,
mu=0.5,
)
self.add(pendulum)
pendulum.start_swinging()
self.wait(20)
class NewSceneName(Scene):
def construct(self):
pass

File diff suppressed because it is too large Load diff

View file

@ -108,10 +108,37 @@ 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 = {
"tex_to_color_map": {"{\\theta}": BLUE}
"tex_to_color_map": {
"{\\theta}": BLUE,
"{\\dot\\theta}": YELLOW,
"{\\ddot\\theta}": RED,
}
}
func = TexMobject(
"{\\theta}(t)", "=",
@ -119,23 +146,24 @@ class ProveTeacherWrong(TeacherStudentsScene):
**tex_config,
)
d_func = TexMobject(
"\\dot {\\theta}(t)", "=",
"{\\dot\\theta}(t)", "=",
"-\\left(\\sqrt{g / L}\\right)",
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
**tex_config,
)
dd_func = TexMobject(
"\\ddot {\\theta}(t)", "=",
"{\\ddot\\theta}(t)", "=",
"-\\left(g / L\\right)",
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
**tex_config,
)
ode = TexMobject(
"\\ddot {\\theta}({t})", "=",
"-\\mu \\dot {\\theta}({t})",
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
**tex_config,
)
# ode = TexMobject(
# "\\ddot {\\theta}({t})", "=",
# "-\\mu \\dot {\\theta}({t})",
# "-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
# **tex_config,
# )
ode = get_ode()
arrows = [TexMobject("\\Downarrow") for x in range(2)]
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
@ -202,6 +230,58 @@ class ProveTeacherWrong(TeacherStudentsScene):
self.wait(8)
class PhysicistPhaseSpace(PiCreatureScene):
def construct(self):
physy = self.pi_creature
name = TextMobject("Physicist")
name.scale(1.5)
name.to_corner(DL, buff=MED_SMALL_BUFF)
physy.next_to(name, UP, SMALL_BUFF)
VGroup(name, physy).shift_onto_screen()
axes = Axes(
x_min=-1,
x_max=10,
y_min=-1,
y_max=7,
)
axes.set_height(6)
axes.next_to(physy, RIGHT)
axes.to_edge(UP)
axes.set_stroke(width=1)
x_label = TextMobject("Position")
x_label.next_to(axes.x_axis.get_right(), UP)
y_label = TextMobject("Momentum")
y_label.next_to(axes.y_axis.get_top(), RIGHT)
title = TextMobject("Phase space")
title.scale(1.5)
title.set_color(YELLOW)
title.move_to(axes)
self.add(name, physy)
self.play(
physy.change, "angry",
Write(axes),
FadeInFromDown(title)
)
self.wait(2)
self.play(
GrowFromPoint(x_label, physy.get_corner(UR)),
physy.change, "raise_right_hand",
axes.x_axis.get_right()
)
self.play(
GrowFromPoint(y_label, physy.get_corner(UR)),
physy.look_at, axes.y_axis.get_top(),
)
self.wait(3)
def create_pi_creature(self):
return PiCreature(color=GREY).to_corner(DL)
class AskAboutActuallySolving(TeacherStudentsScene):
def construct(self):
ode = get_ode()
@ -211,7 +291,7 @@ class AskAboutActuallySolving(TeacherStudentsScene):
self.student_says(
"Yeah yeah, but how do\\\\"
"you acutally \\emph{solve} it?",
"you actually \\emph{solve} it?",
student_index=1,
target_mode="sassy",
added_anims=[morty.change, "thinking"],
@ -242,9 +322,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}"
@ -376,9 +454,62 @@ class HungerForExactness(TeacherStudentsScene):
)
self.wait()
self.wait(3)
self.change_student_modes("tired", "sad", "concerned_musician")
self.wait(4)
self.look_at(solution)
self.wait(5)
self.play(
FadeOutAndShift(solution, 2 * LEFT),
Restore(ode),
self.get_student_changes(*3 * ["sick"])
self.get_student_changes(
"sick", "angry", "tired",
)
)
self.wait(3)
mystery = TexMobject(
"\\theta(t) = ???",
tex_to_color_map={"\\theta": BLUE},
)
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(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):
self.teacher_says("It gets\\\\worse")
self.change_student_modes(
"hesitant", "pleading", "erm"
)
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,
}
@ -35,6 +35,16 @@ def get_ode():
return ode
def get_period_formula():
return TexMobject(
"2\\pi", "\\sqrt{\\,", "L", "/", "g", "}",
tex_to_color_map={
"L": BLUE,
"g": YELLOW,
}
)
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
theta, omega = point[:2]
return np.array([
@ -54,3 +64,55 @@ def get_vector_symbol(*texs, **kwargs):
config.update(kwargs)
array = [[tex] for tex in texs]
return Matrix(array, **config)
def get_heart_var(index):
heart = SuitSymbol("hearts")
if index == 1:
heart.set_color(BLUE_C)
elif index == 2:
heart.set_color(GREEN)
heart.set_height(0.7)
index = Integer(index)
index.move_to(heart.get_corner(DR))
heart.add(index)
return heart
def get_heart_var_deriv(index):
heart = get_heart_var(index)
filler_tex = "T"
deriv = TexMobject("{d", filler_tex, "\\over", "dt}")
deriv.scale(2)
filler = deriv.get_part_by_tex(filler_tex)
heart.match_height(filler)
heart.move_to(filler)
heart.scale(1.5, about_edge=UL)
deriv.remove(filler)
deriv.add(heart)
deriv.heart = heart
return deriv
def get_love_equation1():
equation = VGroup(
get_heart_var_deriv(1),
TexMobject("=").scale(2),
TexMobject("a").scale(2),
get_heart_var(2)
)
equation.arrange(RIGHT)
equation[-1].shift(SMALL_BUFF * DL)
return equation
def get_love_equation2():
equation = VGroup(
get_heart_var_deriv(2),
TexMobject("=").scale(2),
TexMobject("-b").scale(2),
get_heart_var(1),
)
equation.arrange(RIGHT)
equation[-1].shift(SMALL_BUFF * DL)
return equation

File diff suppressed because it is too large Load diff

View file

@ -37,6 +37,40 @@ class SmallAngleApproximationTex(Scene):
class StrogatzQuote(Scene):
def construct(self):
quote = self.get_quote()
movers = VGroup(*quote[:-1].family_members_with_points())
for mover in movers:
mover.save_state()
disc = Circle(radius=0.05)
disc.set_stroke(width=0)
disc.set_fill(BLACK, 0)
disc.move_to(mover)
mover.become(disc)
self.play(
FadeInFrom(quote.author_part, LEFT),
LaggedStartMap(
# FadeInFromLarge,
# quote[:-1].family_members_with_points(),
Restore, movers,
lag_ratio=0.005,
run_time=2,
)
# FadeInFromDown(quote[:-1]),
# lag_ratio=0.01,
)
self.wait()
self.play(
Write(quote.law_part.copy().set_color(YELLOW)),
run_time=1,
)
self.wait()
self.play(
Write(quote.language_part.copy().set_color(BLUE)),
run_time=1.5,
)
self.wait(2)
def get_quote(self):
law_words = "laws of physics"
language_words = "language of differential equations"
author = "-Steven Strogatz"
@ -51,47 +85,342 @@ class StrogatzQuote(Scene):
arg_separator=" ",
substrings_to_isolate=[law_words, language_words, author]
)
law_part = quote.get_part_by_tex(law_words)
language_part = quote.get_part_by_tex(language_words)
author_part = quote.get_part_by_tex(author)
quote.law_part = quote.get_part_by_tex(law_words)
quote.language_part = quote.get_part_by_tex(language_words)
quote.author_part = quote.get_part_by_tex(author)
quote.set_width(12)
quote.to_edge(UP)
quote[-2].shift(SMALL_BUFF * LEFT)
author_part.shift(RIGHT + 0.5 * DOWN)
author_part.scale(1.2, about_edge=UL)
quote.author_part.shift(RIGHT + 0.5 * DOWN)
quote.author_part.scale(1.2, about_edge=UL)
movers = VGroup(*quote[:-1].family_members_with_points())
for mover in movers:
mover.save_state()
disc = Circle(radius=0.05)
disc.set_stroke(width=0)
disc.set_fill(BLACK, 0)
disc.move_to(mover)
mover.become(disc)
self.play(
FadeInFrom(author_part, LEFT),
LaggedStartMap(
# FadeInFromLarge,
# quote[:-1].family_members_with_points(),
Restore, movers,
lag_ratio=0.005,
run_time=2,
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,
}
)
# FadeInFromDown(quote[:-1]),
# lag_ratio=0.01,
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(
Write(law_part.copy().set_color(YELLOW)),
run_time=1,
FadeInFrom(c_word, UL),
GrowArrow(arrows[2]),
FadeInFrom(computation, UP)
)
self.wait()
self.play(
Write(language_part.copy().set_color(BLUE)),
run_time=1.5,
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)
get_angle = angle_tracker.get_value
formula = always_redraw(
lambda: self.get_sine_formula(get_angle())
)
self.add(formula)
self.play(
angle_tracker.set_value, 0,
run_time=3,
)
self.wait()
self.play(
angle_tracker.set_value, 90 * DEGREES,
run_time=3,
)
self.wait()
def get_sine_formula(self, angle):
sin, lp, rp = TexMobject(
"\\sin", "(", ") = "
)
input_part = Integer(
angle / DEGREES,
unit="^\\circ",
)
input_part.set_color(YELLOW)
output_part = DecimalNumber(
np.sin(input_part.get_value() * DEGREES),
num_decimal_places=3,
)
result = VGroup(
sin, lp, input_part, rp, output_part
)
result.arrange(RIGHT, buff=SMALL_BUFF)
sin.scale(1.1, about_edge=DOWN)
lp.align_to(rp, UP)
return result
class SetAsideSeekingSolution(Scene):
def construct(self):
@ -141,6 +470,14 @@ class SetAsideSeekingSolution(Scene):
)
class ThreeBodyTitle(Scene):
def construct(self):
title = TextMobject("Three body problem")
title.scale(1.5)
title.to_edge(UP)
self.add(title)
class ThreeBodySymbols(Scene):
def construct(self):
self.init_coord_groups()
@ -260,3 +597,259 @@ class ThreeBodySymbols(Scene):
rate_func=linear,
)
self.play(FadeOut(coord_copies))
class ThreeBodyEquation(Scene):
def construct(self):
x1 = "\\vec{\\textbf{x}}_1"
x2 = "\\vec{\\textbf{x}}_2"
x3 = "\\vec{\\textbf{x}}_3"
kw = {
"tex_to_color_map": {
x1: RED,
x2: GREEN,
x3: BLUE,
}
}
equations = VGroup(*[
TexMobject(
"{d^2", t1, "\\over dt^2}", "=",
"G", "\\left("
"{" + m2, "(", t2, "-", t1, ")"
"\\over"
"||", t2, "-", t1, "||^3}",
"+",
"{" + m3, "(", t3, "-", t1, ")"
"\\over"
"||", t3, "-", t1, "||^3}",
"\\right)",
**kw
)
for t1, t2, t3, m1, m2, m3 in [
(x1, x2, x3, "m_1", "m_2", "m_3"),
(x2, x3, x1, "m_2", "m_3", "m_1"),
(x3, x1, x2, "m_3", "m_1", "m_2"),
]
])
equations.arrange(DOWN, buff=LARGE_BUFF)
self.play(LaggedStartMap(
FadeInFrom, equations,
lambda m: (m, UP),
lag_ratio=0.2,
))
self.wait()
class JumpToThisPoint(Scene):
def construct(self):
dot = Dot(color=YELLOW)
dot.scale(0.5)
arrow = Vector(DR, color=WHITE)
arrow.next_to(dot, UL, SMALL_BUFF)
words = TextMobject(
"Jump directly to\\\\",
"this point?",
)
words.add_background_rectangle_to_submobjects()
words.next_to(arrow.get_start(), UP, SMALL_BUFF)
self.play(
FadeInFromLarge(dot, 20),
rate_func=rush_into,
)
self.play(
GrowArrow(arrow),
FadeInFromDown(words),
)
class ChaosTitle(Scene):
def construct(self):
title = TextMobject("Chaos theory")
title.scale(1.5)
title.to_edge(UP)
line = Line(LEFT, RIGHT)
line.set_width(FRAME_WIDTH - 1)
line.next_to(title, DOWN)
self.play(
Write(title),
ShowCreation(line),
)
self.wait()
class RevisitQuote(StrogatzQuote, PiCreatureScene):
def construct(self):
quote = self.get_quote()
quote.law_part.set_color(YELLOW)
quote.language_part.set_color(BLUE)
quote.set_stroke(BLACK, 6, background=True)
quote.scale(0.8, about_edge=UL)
new_langauge_part = TextMobject(
"\\Large Language of differential equations"
)
new_langauge_part.to_edge(UP)
new_langauge_part.match_style(quote.language_part)
randy = self.pi_creature
self.play(
FadeInFrom(quote[:-1], DOWN),
FadeInFrom(quote[-1:], LEFT),
randy.change, "raise_right_hand",
)
self.play(Blink(randy))
self.play(randy.change, "angry")
self.play(
Blink(randy),
VFadeOut(randy, run_time=3)
)
mover = VGroup(quote.language_part)
self.add(quote, mover)
self.play(
ReplacementTransform(
mover, new_langauge_part,
),
*[
FadeOut(part)
for part in quote
if part is not quote.language_part
],
run_time=2,
)
self.wait()
class EndScreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"Juan Benet",
"Vassili Philippov",
"Burt Humburg",
"Carlos Vergara Del Rio",
"Matt Russell",
"Scott Gray",
"soekul",
"Tihan Seale",
"Ali Yahya",
"dave nicponski",
"Evan Phillips",
"Graham",
"Joseph Kelly",
"Kaustuv DeBiswas",
"LambdaLabs",
"Lukas Biewald",
"Mike Coleman",
"Peter Mcinerney",
"Quantopian",
"Roy Larson",
"Scott Walter, Ph.D.",
"Yana Chernobilsky",
"Yu Jun",
"Jordan Scales",
"Lukas -krtek.net- Novy",
"John Shaughnessy",
"Britt Selvitelle",
"David Gow",
"J",
"Jonathan Wilson",
"Joseph John Cox",
"Magnus Dahlström",
"Randy C. Will",
"Ryan Atallah",
"Luc Ritchie",
"1stViewMaths",
"Adrian Robinson",
"Alexis Olson",
"Andreas Benjamin Brössel",
"Andrew Busey",
"Ankalagon",
"Antonio Juarez",
"Arjun Chakroborty",
"Art Ianuzzi",
"Awoo",
"Bernd Sing",
"Boris Veselinovich",
"Brian Staroselsky",
"Chad Hurst",
"Charles Southerland",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"Danger Dai",
"Dave B",
"Dave Kester",
"David Clark",
"DeathByShrimp",
"Delton Ding",
"eaglle",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Federico Lebron",
"Giovanni Filippi",
"Hal Hildebrand",
"Herman Dieset",
"Hitoshi Yamauchi",
"Isaac Jeffrey Lee",
"j eduardo perez",
"Jacob Magnuson",
"Jameel Syed",
"Jason Hise",
"Jeff Linse",
"Jeff Straathof",
"John Griffith",
"John Haley",
"John V Wertheim",
"Jonathan Eppele",
"Kai-Siang Ang",
"Kanan Gill",
"L0j1k",
"Lee Redden",
"Linh Tran",
"Ludwig Schubert",
"Magister Mugit",
"Mark B Bahu",
"Mark Heising",
"Martin Price",
"Mathias Jansson",
"Matt Langford",
"Matt Roveto",
"Matthew Cocke",
"Michael Faust",
"Michael Hardel",
"Mirik Gogri",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nero Li",
"Nikita Lesnikov",
"Omar Zrien",
"Owen Campbell-Moore",
"Peter Ehrnstrom",
"RedAgent14",
"rehmi post",
"Richard Burgmann",
"Richard Comish",
"Ripta Pasay",
"Rish Kundalia",
"Robert Teed",
"Roobie",
"Ryan Williams",
"Samuel D. Judge",
"Solara570",
"Stevie Metke",
"Tal Einav",
"Ted Suzman",
"Valeriy Skobelev",
"Xavier Bernard",
"Yavor Ivanov",
"Yaw Etse",
"YinYangBalance.Asia",
"Zach Cardwell",
]
}

View file

@ -0,0 +1,518 @@
from big_ol_pile_of_manim_imports import *
# import scipy
class FourierCirclesScene(Scene):
CONFIG = {
"n_circles": 10,
"big_radius": 2,
"colors": [
BLUE_D,
BLUE_C,
BLUE_E,
GREY_BROWN,
],
"circle_style": {
"stroke_width": 2,
},
"base_frequency": 1,
"slow_factor": 0.25,
"center_point": ORIGIN,
"parametric_function_step_size": 0.001,
}
#
def get_freqs(self):
n = self.n_circles
all_freqs = list(range(n // 2, -n // 2, -1))
all_freqs.sort(key=abs)
return all_freqs
def get_coefficients(self):
return [complex(0) for x in range(self.n_circles)]
def get_color_iterator(self):
return it.cycle(self.colors)
def get_circles(self, freqs=None, coefficients=None):
circles = VGroup()
color_iterator = self.get_color_iterator()
self.center_tracker = VectorizedPoint(self.center_point)
if freqs is None:
freqs = self.get_freqs()
if coefficients is None:
coefficients = self.get_coefficients()
last_circle = None
for freq, coefficient in zip(freqs, coefficients):
if last_circle:
center_func = last_circle.get_start
else:
center_func = self.center_tracker.get_location
circle = self.get_circle(
coefficient=coefficient,
freq=freq,
color=next(color_iterator),
center_func=center_func,
)
circles.add(circle)
last_circle = circle
return circles
def get_circle(self, coefficient, freq, color, center_func):
radius = abs(coefficient)
phase = np.log(coefficient).imag
circle = Circle(
radius=radius,
color=color,
**self.circle_style,
)
circle.radial_line = Line(
circle.get_center(),
circle.get_start(),
color=WHITE,
**self.circle_style,
)
circle.add(circle.radial_line)
circle.freq = freq
circle.phase = phase
circle.rotate(phase)
circle.coefficient = coefficient
circle.center_func = center_func
circle.add_updater(self.update_circle)
return circle
def update_circle(self, circle, dt):
circle.rotate(
self.slow_factor * circle.freq * dt * TAU
)
circle.move_to(circle.center_func())
return circle
def get_rotating_vectors(self, circles):
return VGroup(*[
self.get_rotating_vector(circle)
for circle in circles
])
def get_rotating_vector(self, circle):
vector = Vector(RIGHT, color=WHITE)
vector.add_updater(lambda v: v.put_start_and_end_on(
*circle.radial_line.get_start_and_end()
))
return vector
def get_circle_end_path(self, circles, color=YELLOW):
coefs = [c.coefficient for c in circles]
freqs = [c.freq for c in circles]
center = circles[0].get_center()
path = ParametricFunction(
lambda t: center + reduce(op.add, [
complex_to_R3(
coef * np.exp(TAU * 1j * freq * t)
)
for coef, freq in zip(coefs, freqs)
]),
t_min=0,
t_max=1,
color=color,
step_size=self.parametric_function_step_size,
)
return path
# TODO, this should be a general animated mobect
def get_drawn_path(self, circles, **kwargs):
path = self.get_circle_end_path(circles, **kwargs)
broken_path = CurvesAsSubmobjects(path)
broken_path.curr_time = 0
def update_path(path, dt):
alpha = path.curr_time * self.slow_factor
n_curves = len(path)
for a, sp in zip(np.linspace(0, 1, n_curves), path):
b = alpha - a
if b < 0:
width = 0
else:
width = 2 * (1 - (b % 1))
sp.set_stroke(YELLOW, width=width)
path.curr_time += dt
return path
broken_path.add_updater(update_path)
return broken_path
def get_y_component_wave(self,
circles,
left_x=1,
color=PINK,
n_copies=2,
right_shift_rate=5):
path = self.get_circle_end_path(circles)
wave = ParametricFunction(
lambda t: op.add(
right_shift_rate * t * LEFT,
path.function(t)[1] * UP
),
t_min=path.t_min,
t_max=path.t_max,
color=color,
)
wave_copies = VGroup(*[
wave.copy()
for x in range(n_copies)
])
wave_copies.arrange(RIGHT, buff=0)
top_point = wave_copies.get_top()
wave.creation = ShowCreation(
wave,
run_time=(1 / self.slow_factor),
rate_func=linear,
)
cycle_animation(wave.creation)
wave.add_updater(lambda m: m.shift(
(m.get_left()[0] - left_x) * LEFT
))
def update_wave_copies(wcs):
index = int(
wave.creation.total_time * self.slow_factor
)
wcs[:index].match_style(wave)
wcs[index:].set_stroke(width=0)
wcs.next_to(wave, RIGHT, buff=0)
wcs.align_to(top_point, UP)
wave_copies.add_updater(update_wave_copies)
return VGroup(wave, wave_copies)
def get_wave_y_line(self, circles, wave):
return DashedLine(
circles[-1].get_start(),
wave[0].get_end(),
stroke_width=1,
dash_length=DEFAULT_DASH_LENGTH * 0.5,
)
# Computing Fourier series
def get_coefficients_of_path(self, path, n_samples=10000, freqs=None):
if freqs is None:
freqs = self.get_freqs()
dt = 1 / n_samples
ts = np.arange(0, 1, dt)
samples = np.array([
path.point_from_proportion(t)
for t in ts
])
samples -= self.center_point
complex_samples = samples[:, 0] + 1j * samples[:, 1]
result = []
for freq in freqs:
riemann_sum = np.array([
np.exp(-TAU * 1j * freq * t) * cs
for t, cs in zip(ts, complex_samples)
]).sum() * dt
result.append(riemann_sum)
return result
class FourierSeriesIntroBackground4(FourierCirclesScene):
CONFIG = {
"n_circles": 4,
"center_point": 4 * LEFT,
"run_time": 30,
"big_radius": 1.5,
}
def construct(self):
circles = self.get_circles()
path = self.get_drawn_path(circles)
wave = self.get_y_component_wave(circles)
h_line = always_redraw(
lambda: self.get_wave_y_line(circles, wave)
)
# Why?
circles.update(-1 / self.camera.frame_rate)
#
self.add(circles, path, wave, h_line)
self.wait(self.run_time)
def get_ks(self):
return np.arange(1, 2 * self.n_circles + 1, 2)
def get_freqs(self):
return self.base_frequency * self.get_ks()
def get_coefficients(self):
return self.big_radius / self.get_ks()
class FourierSeriesIntroBackground8(FourierSeriesIntroBackground4):
CONFIG = {
"n_circles": 8,
}
class FourierSeriesIntroBackground12(FourierSeriesIntroBackground4):
CONFIG = {
"n_circles": 12,
}
class FourierSeriesIntroBackground20(FourierSeriesIntroBackground4):
CONFIG = {
"n_circles": 20,
}
class FourierOfPiSymbol(FourierCirclesScene):
CONFIG = {
"n_circles": 50,
"center_point": ORIGIN,
"slow_factor": 0.1,
"run_time": 30,
"tex": "\\pi",
"start_drawn": False,
}
def construct(self):
path = self.get_path()
coefs = self.get_coefficients_of_path(path)
circles = self.get_circles(coefficients=coefs)
for k, circle in zip(it.count(1), circles):
circle.set_stroke(width=max(
1 / np.sqrt(k),
1,
))
# approx_path = self.get_circle_end_path(circles)
drawn_path = self.get_drawn_path(circles)
if self.start_drawn:
drawn_path.curr_time = 1 / self.slow_factor
self.add(path)
self.add(circles)
self.add(drawn_path)
self.wait(self.run_time)
def get_path(self):
tex_mob = TexMobject(self.tex)
tex_mob.set_height(6)
path = tex_mob.family_members_with_points()[0]
path.set_fill(opacity=0)
path.set_stroke(WHITE, 1)
return path
class FourierOfPiSymbol5(FourierOfPiSymbol):
CONFIG = {
"n_circles": 5,
"run_time": 10,
}
class FourierOfTrebleClef(FourierOfPiSymbol):
CONFIG = {
"n_circles": 100,
"run_time": 10,
"start_drawn": True,
}
def get_path(self):
path = SVGMobject("TrebleClef")
path = path.family_members_with_points()[0]
path.set_height(7.5)
path.set_fill(opacity=0)
path.set_stroke(WHITE, 0)
return path
class ExplainCircleAnimations(FourierCirclesScene):
CONFIG = {
# "n_circles": 100,
"n_circles": 20,
"center_point": 2 * DOWN,
"n_top_circles": 9,
# "slow_factor": 0.1,
"path_height": 3,
}
def construct(self):
self.add_path()
self.add_circles()
self.organize_circles_in_a_row()
self.show_frequencies()
self.show_examples_for_frequencies()
self.show_as_vectors()
self.show_vector_sum()
self.moons_of_moons_of_moons()
self.tweak_starting_vectors()
def add_path(self):
self.path = self.get_path()
self.add(self.path)
def add_circles(self):
coefs = self.get_coefficients_of_path(self.path)
self.circles = self.get_circles(coefficients=coefs)
self.add(self.circles)
self.drawn_path = self.get_drawn_path(self.circles)
self.add(self.drawn_path)
self.wait(8)
def organize_circles_in_a_row(self):
circles = self.circles
top_circles = circles[:self.n_top_circles].deepcopy()
center_trackers = VGroup()
for circle in top_circles:
tracker = VectorizedPoint(circle.center_func())
circle.center_func = tracker.get_location
center_trackers.add(tracker)
tracker.freq = circle.freq
tracker.circle = circle
center_trackers.submobjects.sort(
key=lambda m: m.freq
)
center_trackers.generate_target()
right_buff = 1.45
center_trackers.target.arrange(RIGHT, buff=right_buff)
center_trackers.target.to_edge(UP, buff=1.25)
self.add(top_circles)
self.play(
MoveToTarget(center_trackers),
run_time=2
)
self.wait(4)
self.top_circles = top_circles
self.center_trackers = center_trackers
def show_frequencies(self):
center_trackers = self.center_trackers
freq_numbers = VGroup()
for ct in center_trackers:
number = Integer(ct.freq)
number.next_to(ct, DOWN, buff=1)
freq_numbers.add(number)
ct.circle.number = number
ld, rd = [
TexMobject("\\dots")
for x in range(2)
]
ld.next_to(freq_numbers, LEFT, MED_LARGE_BUFF)
rd.next_to(freq_numbers, RIGHT, MED_LARGE_BUFF)
freq_numbers.add_to_back(ld)
freq_numbers.add(rd)
freq_word = TextMobject("Frequencies")
freq_word.scale(1.5)
freq_word.set_color(YELLOW)
freq_word.next_to(freq_numbers, DOWN, MED_LARGE_BUFF)
self.play(
LaggedStartMap(
FadeInFromDown, freq_numbers
)
)
self.play(
Write(freq_word),
LaggedStartMap(
ShowCreationThenFadeAround, freq_numbers,
)
)
self.wait(2)
def show_examples_for_frequencies(self):
top_circles = self.top_circles
c1, c2, c3 = [
list(filter(
lambda c: c.freq == k,
top_circles
))[0]
for k in (1, 2, 3)
]
neg_circles = VGroup(*filter(
lambda c: c.freq < 0,
top_circles
))
for c in [c1, c2, c3, *neg_circles]:
c.rect = SurroundingRectangle(c)
self.play(
ShowCreation(c2.rect),
WiggleOutThenIn(c2.number),
)
self.wait(2)
self.play(
ReplacementTransform(c2.rect, c1.rect),
)
self.play(FadeOut(c1.rect))
self.wait()
self.play(
ShowCreation(c3.rect),
WiggleOutThenIn(c3.number),
)
self.play(
FadeOut(c3.rect),
)
self.wait(2)
self.play(
LaggedStart(*[
ShowCreationThenFadeOut(c.rect)
for c in neg_circles
])
)
self.wait(3)
def show_as_vectors(self):
top_circles = self.top_circles
top_vectors = self.get_rotating_vectors(top_circles)
self.play(
top_circles.set_stroke, {"width": 0.5},
FadeIn(top_vectors),
)
self.wait(3)
self.top_vectors = top_vectors
def show_vector_sum(self):
top_circles = self.top_circles
top_vectors = self.top_vectors
self.play(
FadeOut(self.path),
FadeOut(self.circles),
)
def moons_of_moons_of_moons(self):
pass
def tweak_starting_vectors(self):
pass
#
def get_path(self):
tex = TexMobject("f")
path = tex.family_members_with_points()[0]
path.set_stroke(WHITE, 1)
path.set_fill(opacity=0)
path.set_height(self.path_height)
path.move_to(self.center_point)
return path
# return Square().set_height(3)

View file

@ -0,0 +1,6 @@
from big_ol_pile_of_manim_imports import *
class NewSceneName(Scene):
def construct(self):
pass

View file

@ -0,0 +1,22 @@
from big_ol_pile_of_manim_imports import *
class FourierSeriesIntro(Scene):
def construct(self):
title = TextMobject(
"Fourier ", "Series", ":",
" An origin story",
arg_separator="",
)
title.scale(2)
title.to_edge(UP)
image = ImageMobject("Joseph Fourier")
image.set_height(5)
image.next_to(title, DOWN, MED_LARGE_BUFF)
image.to_edge(LEFT)
name = TextMobject("Joseph", "Fourier")
name.next_to(image, DOWN)
self.add(title)
self.add(image)
self.add(name)

View file

@ -0,0 +1,63 @@
import numpy as np
# Physical constants
g = 9.8
L = 2
mu = 0.1
THETA_0 = np.pi / 3 # 60 degrees
THETA_DOT_0 = 0 # No initial angular velocity
# Definition of ODE
def get_theta_double_dot(theta, theta_dot):
return -mu * theta_dot - (g / L) * np.sin(theta)
# Solution to the differential equation
def theta(t):
# Initialize changing values
theta = THETA_0
theta_dot = THETA_DOT_0
delta_t = 0.01 # Some time step
for time in np.arange(0, t, delta_t):
# Take many little time steps of size delta_t
# until the total time is the function's input
theta_double_dot = get_theta_double_dot(
theta, theta_dot
)
theta += theta_dot * delta_t
theta_dot += theta_double_dot * delta_t
return theta

View file

@ -151,8 +151,11 @@ class LaggedStartMap(LaggedStart):
args_list.append(arg_creator(submob))
else:
args_list.append((submob,))
anim_kwargs = dict(kwargs)
if "lag_ratio" in anim_kwargs:
anim_kwargs.pop("lag_ratio")
animations = [
AnimationClass(*args, **kwargs)
AnimationClass(*args, **anim_kwargs)
for args in args_list
]
super().__init__(*animations, **kwargs)

View file

@ -102,7 +102,7 @@ class PatreonThanks(Scene):
patreon_logo = PatreonLogo()
patreon_logo.to_edge(UP)
patrons = list(map(TextMobject, self.specific_patrons))
patrons = list(map(TextMobject, self.specific_patronds))
num_groups = float(len(patrons)) / self.max_patron_group_size
proportion_range = np.linspace(0, 1, num_groups + 1)
indices = (len(patrons) * proportion_range).astype('int')
@ -213,7 +213,14 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
underline.next_to(thanks, DOWN, SMALL_BUFF)
thanks.add(underline)
patrons = VGroup(*list(map(TextMobject, self.specific_patrons)))
changed_patron_names = map(
self.modify_patron_name,
self.specific_patrons,
)
patrons = VGroup(*map(
TextMobject,
changed_patron_names,
))
patrons.scale(self.patron_scale_val)
for patron in patrons:
if patron.get_width() > self.max_patron_width:
@ -252,6 +259,11 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
self.add(columns, black_rect, line, thanks)
self.wait(wait_time)
def modify_patron_name(self, name):
if name is "RedAgent14":
return "Brian Shepetofsky"
return name
class LogoGenerationTemplate(MovingCameraScene):
def setup(self):

View file

@ -174,6 +174,7 @@ class PiCreatureScene(Scene):
continue
anims_with_pi_creature = [anim for anim in animations if pi_creature in anim.mobject.get_family()]
for anim in anims_with_pi_creature:
continue # TODO, this is broken
if isinstance(anim, Transform):
index = anim.mobject.get_family().index(pi_creature)
target_family = anim.target_mobject.get_family()

View file

@ -8,6 +8,9 @@ class AnimatedBoundary(VGroup):
"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
"max_stroke_width": 3,
"cycle_rate": 0.5,
"back_and_forth": True,
"draw_rate_func": smooth,
"fade_rate_func": smooth,
}
def __init__(self, vmobject, **kwargs):
@ -22,9 +25,11 @@ class AnimatedBoundary(VGroup):
]
self.add(*self.boundary_copies)
self.total_time = 0
self.add_updater(lambda m, dt: self.update(dt))
self.add_updater(
lambda m, dt: self.update_boundary_copies(dt)
)
def update(self, dt):
def update_boundary_copies(self, dt):
# Not actual time, but something which passes at
# an altered rate to make the implementation below
# cleaner
@ -35,12 +40,14 @@ class AnimatedBoundary(VGroup):
vmobject = self.vmobject
index = int(time % len(colors))
alpha = smooth(time % 1)
alpha = time % 1
draw_alpha = self.draw_rate_func(alpha)
fade_alpha = self.fade_rate_func(alpha)
if int(time) % 2 == 0:
bounds = (0, alpha)
if self.back_and_forth and int(time) % 1 == 0:
bounds = (1 - draw_alpha, 1)
else:
bounds = (1 - alpha, 1)
bounds = (0, draw_alpha)
self.full_family_become_partial(growing, vmobject, *bounds)
growing.set_stroke(colors[index], width=msw)
@ -48,7 +55,7 @@ class AnimatedBoundary(VGroup):
self.full_family_become_partial(fading, vmobject, 0, 1)
fading.set_stroke(
color=colors[index - 1],
width=(1 - alpha) * msw
width=(1 - fade_alpha) * msw
)
self.total_time += dt

View file

@ -82,7 +82,7 @@ class CoordinateSystem():
)[0],
target=x,
lower_bound=self.x_min,
uplper_bound=self.x_max,
upper_bound=self.x_max,
)
if alpha is not None:
return graph.point_from_proportion(alpha)

View file

@ -22,7 +22,7 @@ def always(method, *args, **kwargs):
def f_always(method, *arg_generators, **kwargs):
"""
More functional version of always, where insetead
More functional version of always, where instead
of taking in args, it takes in functions which ouput
the relevant arguments.
"""
@ -77,13 +77,12 @@ def turn_animation_into_updater(animation, cycle=False, **kwargs):
def update(m, dt):
run_time = animation.get_run_time()
alpha = np.clip(
animation.total_time / run_time,
0, 1,
)
time_ratio = animation.total_time / run_time
if cycle:
animation.total_time = animation.total_time % run_time
elif alpha >= 1:
alpha = time_ratio % 1
else:
alpha = np.clip(time_ratio, 0, 1)
if alpha >= 1:
animation.finish()
m.remove_updater(update)
return

View file

@ -95,9 +95,10 @@ class NumberLine(Line):
)
def get_tick_numbers(self):
u = -1 if self.include_tip else 1
return np.arange(
self.leftmost_tick,
self.x_max + self.tick_frequency / 2,
self.x_max + u * self.tick_frequency / 2,
self.tick_frequency
)

View file

@ -393,11 +393,13 @@ class ClockPassesTime(Animation):
radians=hour_radians,
**rot_kwargs
)
self.hour_rotation.begin()
self.minute_rotation = Rotating(
clock.minute_hand,
radians=12 * hour_radians,
**rot_kwargs
)
self.minute_rotation.begin()
Animation.__init__(self, clock, **kwargs)
def interpolate_mobject(self, alpha):

View file

@ -2817,18 +2817,16 @@ class ShowTwoPopulations(Scene):
num_foxes = Integer(10)
num_foxes.scale(self.count_word_scale_val)
num_foxes.next_to(labels[0], RIGHT)
num_foxes.align_to(labels[0][1], DOWN)
num_foxes.align_to(labels[0][0][1], DOWN)
num_rabbits = Integer(10)
num_rabbits.scale(self.count_word_scale_val)
num_rabbits.next_to(labels[1], RIGHT)
num_rabbits.align_to(labels[1][1], DOWN)
num_rabbits.align_to(labels[1][0][1], DOWN)
self.add(ContinualChangingDecimal(
num_foxes, lambda a: get_num_foxes()
))
self.add(ContinualChangingDecimal(
num_rabbits, lambda a: get_num_rabbits()
))
num_foxes.add_updater(lambda d: d.set_value(get_num_foxes()))
num_rabbits.add_updater(lambda d: d.set_value(get_num_rabbits()))
self.add(num_foxes, num_rabbits)
for count in num_foxes, num_rabbits:
self.add(Mobject.add_updater(
@ -2853,13 +2851,13 @@ class ShowTwoPopulations(Scene):
height=self.animal_height,
fill_color=color,
)
for submob in result.family_members_with_points():
if submob.is_subpath:
submob.is_subpath = False
submob.set_fill(
interpolate_color(color, BLACK, 0.8),
opacity=1
)
# for submob in result.family_members_with_points():
# if submob.is_subpath:
# submob.is_subpath = False
# submob.set_fill(
# interpolate_color(color, BLACK, 0.8),
# opacity=1
# )
x_shift, y_shift = [
(2 * random.random() - 1) * max_val
for max_val in [
@ -3021,25 +3019,7 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
self.pop_sizes_updates = pop_sizes_updates
def write_differential_equations(self):
variables = ["X", "YY"]
equations = TexMobject(
"""
{dX \\over dt} =
X \\cdot (\\alpha - \\beta YY \\,) \\\\
\\quad \\\\
{dYY \\over dt} =
YY \\cdot (\\delta X - \\gamma)
""",
substrings_to_isolate=variables
)
animals = [self.get_rabbit(), self.get_fox().flip()]
for char, animal in zip(variables, animals):
for part in equations.get_parts_by_tex(char):
animal_copy = animal.copy()
animal_copy.set_height(0.5)
animal_copy.move_to(part, DL)
Transform(part, animal_copy).update(1)
equations = self.get_equations()
equations.shift(2 * DOWN)
rect = SurroundingRectangle(equations, color=YELLOW)
rect.set_fill(BLACK, 0.8)
@ -3183,6 +3163,28 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
)
self.wait(self.flow_time)
#
def get_equations(self):
variables = ["X", "YY"]
equations = TexMobject(
"""
{dX \\over dt} =
X \\cdot (\\alpha - \\beta YY \\,) \\\\
\\quad \\\\
{dYY \\over dt} =
YY \\cdot (\\delta X - \\gamma)
""",
substrings_to_isolate=variables
)
animals = [self.get_rabbit(), self.get_fox().flip()]
for char, animal in zip(variables, animals):
for part in equations.get_parts_by_tex(char):
animal_copy = animal.copy()
animal_copy.set_height(0.5)
animal_copy.move_to(part, DL)
part.become(animal_copy)
return equations
class PhaseFlowWords(Scene):
def construct(self):

View file

@ -43,7 +43,7 @@ def stage_scenes(module_name):
# }
# TODO, fix this
animation_dir = os.path.join(
VIDEO_DIR, "clacks_solution2", "1440p60"
VIDEO_DIR, "ode", "part1", "1440p60"
)
#
files = os.listdir(animation_dir)