mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 11:27:45 +00:00
commit
8be321d104
22 changed files with 4879 additions and 520 deletions
|
|
@ -6,8 +6,11 @@ from active_projects.ode.part1.wordy_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part1"
|
OUTPUT_DIRECTORY = "ode/part1"
|
||||||
ALL_SCENE_CLASSES = [
|
ALL_SCENE_CLASSES = [
|
||||||
|
WhenChangeIsEasier,
|
||||||
|
VectorFieldTest,
|
||||||
IntroducePendulum,
|
IntroducePendulum,
|
||||||
MultiplePendulumsOverlayed,
|
MultiplePendulumsOverlayed,
|
||||||
|
PeriodFormula,
|
||||||
FormulasAreLies,
|
FormulasAreLies,
|
||||||
MediumAnglePendulum,
|
MediumAnglePendulum,
|
||||||
MediumHighAnglePendulum,
|
MediumHighAnglePendulum,
|
||||||
|
|
@ -18,21 +21,57 @@ ALL_SCENE_CLASSES = [
|
||||||
VeryLowAnglePendulum,
|
VeryLowAnglePendulum,
|
||||||
FormulasAreLies,
|
FormulasAreLies,
|
||||||
TourOfDifferentialEquations,
|
TourOfDifferentialEquations,
|
||||||
|
WherePendulumLeads,
|
||||||
|
LongDoublePendulum,
|
||||||
# FollowThisThread,
|
# FollowThisThread,
|
||||||
StrogatzQuote,
|
StrogatzQuote,
|
||||||
|
ShowHorizontalDashedLine,
|
||||||
|
RabbitFoxPopulations,
|
||||||
|
RabbitFoxEquation,
|
||||||
# Something...
|
# Something...
|
||||||
|
ShowSimpleTrajectory,
|
||||||
|
SimpleProjectileEquation,
|
||||||
|
SimpleProjectileEquationVGraphFreedom,
|
||||||
ShowGravityAcceleration,
|
ShowGravityAcceleration,
|
||||||
|
UniversalGravityLawSymbols,
|
||||||
|
ExampleTypicalODE,
|
||||||
AnalyzePendulumForce,
|
AnalyzePendulumForce,
|
||||||
|
ShowSineValues,
|
||||||
BuildUpEquation,
|
BuildUpEquation,
|
||||||
|
AirResistanceBrace,
|
||||||
ShowDerivativeVideo,
|
ShowDerivativeVideo,
|
||||||
SubtleAirCurrents,
|
SubtleAirCurrents,
|
||||||
|
SimpleDampenedPendulum,
|
||||||
DefineODE,
|
DefineODE,
|
||||||
|
SecondOrderEquationExample,
|
||||||
ODEvsPDEinFrames,
|
ODEvsPDEinFrames,
|
||||||
ProveTeacherWrong,
|
ProveTeacherWrong,
|
||||||
SetAsideSeekingSolution,
|
SetAsideSeekingSolution,
|
||||||
ReferencePiCollisionStateSpaces,
|
#
|
||||||
|
WriteInRadians,
|
||||||
|
XEqLThetaToCorner,
|
||||||
|
ComingUp,
|
||||||
|
InputLabel,
|
||||||
|
SoWhatIsThetaThen,
|
||||||
|
ReallyHardToSolve,
|
||||||
|
ReasonForSolution,
|
||||||
|
PhysicistPhaseSpace,
|
||||||
|
GleickQuote,
|
||||||
|
SpectrumOfStartingStates,
|
||||||
|
WritePhaseFlow,
|
||||||
|
AskAboutStability,
|
||||||
|
LoveExample,
|
||||||
|
PassageOfTime,
|
||||||
|
LovePhaseSpace,
|
||||||
|
ComparePhysicsToLove,
|
||||||
|
FramesComparingPhysicsToLove,
|
||||||
|
SetupToTakingManyTinySteps,
|
||||||
|
ShowClutterPrevention,
|
||||||
|
# VisualizeHeightSlopeCurvature,
|
||||||
VisualizeStates,
|
VisualizeStates,
|
||||||
|
ReferencePiCollisionStateSpaces,
|
||||||
IntroduceVectorField,
|
IntroduceVectorField,
|
||||||
|
XComponentArrows,
|
||||||
BreakingSecondOrderIntoTwoFirstOrder,
|
BreakingSecondOrderIntoTwoFirstOrder,
|
||||||
ShowPendulumPhaseFlow,
|
ShowPendulumPhaseFlow,
|
||||||
ShowHighVelocityCase,
|
ShowHighVelocityCase,
|
||||||
|
|
@ -42,10 +81,27 @@ ALL_SCENE_CLASSES = [
|
||||||
LorenzVectorField,
|
LorenzVectorField,
|
||||||
ThreeBodiesInSpace,
|
ThreeBodiesInSpace,
|
||||||
AltThreeBodiesInSpace,
|
AltThreeBodiesInSpace,
|
||||||
|
TwoBodiesInSpace,
|
||||||
|
TwoBodiesWithZPart,
|
||||||
|
ThreeBodyTitle,
|
||||||
ThreeBodySymbols,
|
ThreeBodySymbols,
|
||||||
|
#
|
||||||
|
HighAmplitudePendulum,
|
||||||
|
WritePhaseSpace,
|
||||||
|
#
|
||||||
AskAboutActuallySolving,
|
AskAboutActuallySolving,
|
||||||
WriteODESolvingCode,
|
WriteODESolvingCode,
|
||||||
TakeManyTinySteps,
|
TakeManyTinySteps,
|
||||||
|
ManyStepsFromDifferentStartingPoints,
|
||||||
InaccurateComputation,
|
InaccurateComputation,
|
||||||
HungerForExactness,
|
HungerForExactness,
|
||||||
|
ShowRect,
|
||||||
|
ShowSquare,
|
||||||
|
JumpToThisPoint,
|
||||||
|
ThreeBodyEquation,
|
||||||
|
ItGetsWorse,
|
||||||
|
ChaosTitle,
|
||||||
|
RevisitQuote,
|
||||||
|
EndScreen,
|
||||||
|
Thumbnail,
|
||||||
]
|
]
|
||||||
|
|
|
||||||
19
active_projects/ode/all_part2_scenes.py
Normal file
19
active_projects/ode/all_part2_scenes.py
Normal 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,
|
||||||
|
]
|
||||||
|
|
@ -287,6 +287,9 @@ class ThetaVsTAxes(Axes):
|
||||||
y_axis.label = theta_label
|
y_axis.label = theta_label
|
||||||
y_axis.add(theta_label)
|
y_axis.add(theta_label)
|
||||||
|
|
||||||
|
self.y_axis_label = theta_label
|
||||||
|
self.x_axis_label = t_label
|
||||||
|
|
||||||
x_axis.add_numbers()
|
x_axis.add_numbers()
|
||||||
y_axis.add(self.get_y_axis_coordinates(y_axis))
|
y_axis.add(self.get_y_axis_coordinates(y_axis))
|
||||||
|
|
||||||
|
|
@ -357,6 +360,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
"length": 3,
|
"length": 3,
|
||||||
"top_point": 4 * RIGHT,
|
"top_point": 4 * RIGHT,
|
||||||
"weight_diameter": 0.35,
|
"weight_diameter": 0.35,
|
||||||
|
"gravity": 20,
|
||||||
},
|
},
|
||||||
"theta_vs_t_axes_config": {
|
"theta_vs_t_axes_config": {
|
||||||
"y_max": PI / 4,
|
"y_max": PI / 4,
|
||||||
|
|
@ -366,6 +370,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
"unit_size": 2,
|
"unit_size": 2,
|
||||||
"tip_length": 0.3,
|
"tip_length": 0.3,
|
||||||
},
|
},
|
||||||
|
"x_max": 12,
|
||||||
"number_line_config": {
|
"number_line_config": {
|
||||||
"stroke_width": 2,
|
"stroke_width": 2,
|
||||||
}
|
}
|
||||||
|
|
@ -378,12 +383,13 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.add_pendulum()
|
self.add_pendulum()
|
||||||
self.label_pi_creatures()
|
# self.label_pi_creatures()
|
||||||
self.label_pendulum()
|
self.label_pendulum()
|
||||||
self.add_graph()
|
self.add_graph()
|
||||||
|
self.label_function()
|
||||||
self.show_graph_period()
|
self.show_graph_period()
|
||||||
self.show_length_and_gravity()
|
self.show_length_and_gravity()
|
||||||
self.tweak_length_and_gravity()
|
# self.tweak_length_and_gravity()
|
||||||
|
|
||||||
def create_pi_creatures(self):
|
def create_pi_creatures(self):
|
||||||
randy = Randolph(color=BLUE_C)
|
randy = Randolph(color=BLUE_C)
|
||||||
|
|
@ -437,7 +443,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
GrowArrow(morty_label.arrow),
|
GrowArrow(morty_label.arrow),
|
||||||
morty.change, "raise_right_hand",
|
morty.change, "raise_right_hand",
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait(2)
|
||||||
|
|
||||||
def label_pendulum(self):
|
def label_pendulum(self):
|
||||||
pendulum = self.pendulum
|
pendulum = self.pendulum
|
||||||
|
|
@ -446,18 +452,18 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
rect = SurroundingRectangle(label, buff=0.5 * SMALL_BUFF)
|
rect = SurroundingRectangle(label, buff=0.5 * SMALL_BUFF)
|
||||||
rect.add_updater(lambda r: r.move_to(label))
|
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(
|
self.play(
|
||||||
ShowCreationThenFadeOut(rect),
|
ShowCreationThenFadeOut(rect),
|
||||||
ShowCreationThenDestruction(
|
|
||||||
label.copy().set_style(
|
|
||||||
fill_opacity=0,
|
|
||||||
stroke_color=PINK,
|
|
||||||
stroke_width=2,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
randy.change, "pondering",
|
|
||||||
morty.change, "pondering",
|
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
|
@ -467,7 +473,10 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
axes.to_corner(UL)
|
axes.to_corner(UL)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
Restore(self.camera_frame),
|
Restore(
|
||||||
|
self.camera_frame,
|
||||||
|
rate_func=squish_rate_func(smooth, 0, 0.9),
|
||||||
|
),
|
||||||
DrawBorderThenFill(
|
DrawBorderThenFill(
|
||||||
axes,
|
axes,
|
||||||
rate_func=squish_rate_func(smooth, 0.5, 1),
|
rate_func=squish_rate_func(smooth, 0.5, 1),
|
||||||
|
|
@ -481,10 +490,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
),
|
),
|
||||||
run_time=3,
|
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)
|
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)
|
self.wait(4)
|
||||||
|
|
||||||
def show_graph_period(self):
|
def show_graph_period(self):
|
||||||
|
|
@ -503,13 +532,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
line.shift(SMALL_BUFF * RIGHT)
|
line.shift(SMALL_BUFF * RIGHT)
|
||||||
brace = Brace(line, UP, buff=SMALL_BUFF)
|
brace = Brace(line, UP, buff=SMALL_BUFF)
|
||||||
brace.add_to_back(brace.copy().set_style(BLACK, 10))
|
brace.add_to_back(brace.copy().set_style(BLACK, 10))
|
||||||
formula = TexMobject(
|
formula = get_period_formula()
|
||||||
"\\sqrt{\\,", "2\\pi", "L", "/", "g", "}",
|
|
||||||
tex_to_color_map={
|
|
||||||
"L": BLUE,
|
|
||||||
"g": YELLOW,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
formula.next_to(brace, UP, SMALL_BUFF)
|
formula.next_to(brace, UP, SMALL_BUFF)
|
||||||
|
|
||||||
self.period_formula = formula
|
self.period_formula = formula
|
||||||
|
|
@ -537,29 +560,22 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
self.pendulum,
|
self.pendulum,
|
||||||
length_multiple=0.5 / 9.8,
|
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(
|
self.play(
|
||||||
|
ShowCreationThenDestructionAround(L),
|
||||||
ShowCreation(new_rod),
|
ShowCreation(new_rod),
|
||||||
dot.move_to, new_rod,
|
|
||||||
dot.fade, 1,
|
|
||||||
)
|
)
|
||||||
self.remove(dot)
|
|
||||||
self.play(FadeOut(new_rod))
|
self.play(FadeOut(new_rod))
|
||||||
self.wait()
|
|
||||||
|
|
||||||
self.play(ShowCreationThenDestructionAround(g))
|
|
||||||
dot.move_to(g)
|
|
||||||
dot.set_fill(opacity=0.5)
|
|
||||||
self.play(
|
self.play(
|
||||||
|
ShowCreationThenDestructionAround(g),
|
||||||
GrowArrow(g_vect),
|
GrowArrow(g_vect),
|
||||||
dot.move_to, g_vect,
|
|
||||||
dot.fade, 1,
|
|
||||||
)
|
)
|
||||||
self.remove(dot)
|
self.play(self.get_down_vectors_animation(down_vectors))
|
||||||
self.wait(2)
|
self.wait(6)
|
||||||
|
|
||||||
self.gravity_vector = g_vect
|
self.gravity_vector = g_vect
|
||||||
|
|
||||||
|
|
@ -593,15 +609,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
# new_pendulum_config["initial_theta"] = pendulum.get_theta()
|
# new_pendulum_config["initial_theta"] = pendulum.get_theta()
|
||||||
new_pendulum = Pendulum(**new_pendulum_config)
|
new_pendulum = Pendulum(**new_pendulum_config)
|
||||||
|
|
||||||
down_vectors = VGroup(*[
|
down_vectors = self.get_down_vectors()
|
||||||
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)
|
|
||||||
|
|
||||||
self.play(randy.change, "happy")
|
self.play(randy.change, "happy")
|
||||||
self.play(
|
self.play(
|
||||||
|
|
@ -624,10 +632,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
g_vect.scale(2)
|
g_vect.scale(2)
|
||||||
self.play(
|
self.play(
|
||||||
FadeOut(graph2),
|
FadeOut(graph2),
|
||||||
LaggedStart(*[
|
self.get_down_vectors_animation(down_vectors)
|
||||||
GrowArrow(v, rate_func=there_and_back)
|
|
||||||
for v in down_vectors
|
|
||||||
], lag_ratio=0.0005, run_time=2, remover=True)
|
|
||||||
)
|
)
|
||||||
self.play(
|
self.play(
|
||||||
FadeIn(graph3),
|
FadeIn(graph3),
|
||||||
|
|
@ -635,6 +640,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
||||||
)
|
)
|
||||||
self.wait(6)
|
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):
|
class MultiplePendulumsOverlayed(Scene):
|
||||||
CONFIG = {
|
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):
|
class AnalyzePendulumForce(MovingCameraScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"pendulum_config": {
|
"pendulum_config": {
|
||||||
|
|
@ -885,19 +1003,20 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.add_pendulum()
|
self.add_pendulum()
|
||||||
|
self.show_arc_length()
|
||||||
self.add_g_vect()
|
self.add_g_vect()
|
||||||
self.show_constraint()
|
self.show_constraint()
|
||||||
self.break_g_vect_into_components()
|
self.break_g_vect_into_components()
|
||||||
self.show_gsin_formula()
|
|
||||||
self.show_acceleration_at_different_angles()
|
|
||||||
self.show_angle_geometry()
|
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.emphasize_theta()
|
||||||
self.show_arc_length()
|
# self.show_angular_velocity()
|
||||||
self.show_angular_velocity()
|
# self.show_angular_acceleration()
|
||||||
self.show_angular_acceleration()
|
# self.circle_g_sin_formula()
|
||||||
self.circle_g_sin_formula()
|
|
||||||
|
|
||||||
def add_pendulum(self):
|
def add_pendulum(self):
|
||||||
pendulum = Pendulum(**self.pendulum_config)
|
pendulum = Pendulum(**self.pendulum_config)
|
||||||
|
|
@ -910,6 +1029,97 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
self.pendulum = pendulum
|
self.pendulum = pendulum
|
||||||
self.theta_tracker = theta_tracker
|
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):
|
def add_g_vect(self):
|
||||||
pendulum = self.pendulum
|
pendulum = self.pendulum
|
||||||
|
|
||||||
|
|
@ -927,15 +1137,10 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
GrowArrow(g_vect),
|
GrowArrow(g_vect),
|
||||||
FadeInFrom(g_word, UP, lag_ratio=0.1),
|
FadeInFrom(g_word, UP, lag_ratio=0.1),
|
||||||
)
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
def show_constraint(self):
|
def show_constraint(self):
|
||||||
pendulum = self.pendulum
|
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()
|
arcs = VGroup()
|
||||||
for u in [-1, 2, -1]:
|
for u in [-1, 2, -1]:
|
||||||
|
|
@ -946,7 +1151,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
radius=pendulum.length,
|
radius=pendulum.length,
|
||||||
arc_center=pendulum.get_fixed_point(),
|
arc_center=pendulum.get_fixed_point(),
|
||||||
stroke_width=2,
|
stroke_width=2,
|
||||||
stroke_color=GREEN,
|
stroke_color=YELLOW,
|
||||||
stroke_opacity=0.5,
|
stroke_opacity=0.5,
|
||||||
)
|
)
|
||||||
self.play(
|
self.play(
|
||||||
|
|
@ -954,9 +1159,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
ShowCreation(arc)
|
ShowCreation(arc)
|
||||||
)
|
)
|
||||||
arcs.add(arc)
|
arcs.add(arc)
|
||||||
self.wait()
|
self.play(FadeOut(arcs))
|
||||||
|
|
||||||
self.traced_arcs = arcs
|
|
||||||
|
|
||||||
def break_g_vect_into_components(self):
|
def break_g_vect_into_components(self):
|
||||||
g_vect = self.g_vect
|
g_vect = self.g_vect
|
||||||
|
|
@ -983,6 +1186,52 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
self.play(GrowArrow(g_vect.perp))
|
self.play(GrowArrow(g_vect.perp))
|
||||||
self.wait()
|
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):
|
def show_gsin_formula(self):
|
||||||
g_vect = self.g_vect
|
g_vect = self.g_vect
|
||||||
g_word = self.g_word
|
g_word = self.g_word
|
||||||
|
|
@ -1037,27 +1286,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
self.g_sin_label = g_sin_label
|
self.g_sin_label = g_sin_label
|
||||||
self.g_cos_label = g_cos_label
|
self.g_cos_label = g_cos_label
|
||||||
|
|
||||||
def show_acceleration_at_different_angles(self):
|
def show_sign(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))
|
|
||||||
|
|
||||||
get_theta = self.pendulum.get_theta
|
get_theta = self.pendulum.get_theta
|
||||||
theta_decimal = DecimalNumber(include_sign=True)
|
theta_decimal = DecimalNumber(include_sign=True)
|
||||||
theta_decimal.add_updater(lambda d: d.set_value(
|
theta_decimal.add_updater(lambda d: d.set_value(
|
||||||
|
|
@ -1070,84 +1299,148 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
GREEN if get_theta() > 0 else RED
|
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.set_theta(0)
|
||||||
self.wait(2)
|
self.wait(2)
|
||||||
self.set_theta(89.9 * DEGREES, run_time=3)
|
self.set_theta(89.9 * DEGREES, run_time=3)
|
||||||
self.wait(2)
|
self.wait(2)
|
||||||
self.set_theta(60 * DEGREES, run_time=2)
|
self.set_theta(
|
||||||
self.wait()
|
60 * DEGREES,
|
||||||
|
FadeIn(self.x_eq),
|
||||||
self.play(FadeInFrom(theta_decimal, UP))
|
run_time=2,
|
||||||
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.wait()
|
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):
|
def ask_about_what_to_do(self):
|
||||||
g_vect = self.g_vect
|
g_vect = self.g_vect
|
||||||
|
|
@ -1215,103 +1508,22 @@ class AnalyzePendulumForce(MovingCameraScene):
|
||||||
|
|
||||||
self.play(FocusOn(pendulum.theta_label))
|
self.play(FocusOn(pendulum.theta_label))
|
||||||
self.play(Indicate(pendulum.theta_label))
|
self.play(Indicate(pendulum.theta_label))
|
||||||
old_updaters = pendulum.get_updaters()
|
|
||||||
pendulum.clear_updaters(recursive=False)
|
pendulum_copy = pendulum.deepcopy()
|
||||||
pendulum.start_swinging()
|
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)
|
self.wait(5)
|
||||||
pendulum.clear_updaters()
|
pendulum_copy.end_swinging()
|
||||||
for updater in old_updaters:
|
self.remove(pendulum_copy)
|
||||||
pendulum.add_updater(updater)
|
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):
|
def show_angular_velocity(self):
|
||||||
pass
|
pass
|
||||||
|
|
@ -1664,6 +1876,18 @@ class BuildUpEquation(Scene):
|
||||||
self.wait()
|
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):
|
class NewSceneName(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -108,10 +108,37 @@ class FormulasAreLies(PiCreatureScene):
|
||||||
# pass
|
# 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):
|
class ProveTeacherWrong(TeacherStudentsScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
tex_config = {
|
tex_config = {
|
||||||
"tex_to_color_map": {"{\\theta}": BLUE}
|
"tex_to_color_map": {
|
||||||
|
"{\\theta}": BLUE,
|
||||||
|
"{\\dot\\theta}": YELLOW,
|
||||||
|
"{\\ddot\\theta}": RED,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func = TexMobject(
|
func = TexMobject(
|
||||||
"{\\theta}(t)", "=",
|
"{\\theta}(t)", "=",
|
||||||
|
|
@ -119,23 +146,24 @@ class ProveTeacherWrong(TeacherStudentsScene):
|
||||||
**tex_config,
|
**tex_config,
|
||||||
)
|
)
|
||||||
d_func = TexMobject(
|
d_func = TexMobject(
|
||||||
"\\dot {\\theta}(t)", "=",
|
"{\\dot\\theta}(t)", "=",
|
||||||
"-\\left(\\sqrt{g / L}\\right)",
|
"-\\left(\\sqrt{g / L}\\right)",
|
||||||
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
|
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
|
||||||
**tex_config,
|
**tex_config,
|
||||||
)
|
)
|
||||||
dd_func = TexMobject(
|
dd_func = TexMobject(
|
||||||
"\\ddot {\\theta}(t)", "=",
|
"{\\ddot\\theta}(t)", "=",
|
||||||
"-\\left(g / L\\right)",
|
"-\\left(g / L\\right)",
|
||||||
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||||
**tex_config,
|
**tex_config,
|
||||||
)
|
)
|
||||||
ode = TexMobject(
|
# ode = TexMobject(
|
||||||
"\\ddot {\\theta}({t})", "=",
|
# "\\ddot {\\theta}({t})", "=",
|
||||||
"-\\mu \\dot {\\theta}({t})",
|
# "-\\mu \\dot {\\theta}({t})",
|
||||||
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
# "-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||||
**tex_config,
|
# **tex_config,
|
||||||
)
|
# )
|
||||||
|
ode = get_ode()
|
||||||
arrows = [TexMobject("\\Downarrow") for x in range(2)]
|
arrows = [TexMobject("\\Downarrow") for x in range(2)]
|
||||||
|
|
||||||
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
|
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
|
||||||
|
|
@ -202,6 +230,58 @@ class ProveTeacherWrong(TeacherStudentsScene):
|
||||||
self.wait(8)
|
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):
|
class AskAboutActuallySolving(TeacherStudentsScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
ode = get_ode()
|
ode = get_ode()
|
||||||
|
|
@ -211,7 +291,7 @@ class AskAboutActuallySolving(TeacherStudentsScene):
|
||||||
|
|
||||||
self.student_says(
|
self.student_says(
|
||||||
"Yeah yeah, but how do\\\\"
|
"Yeah yeah, but how do\\\\"
|
||||||
"you acutally \\emph{solve} it?",
|
"you actually \\emph{solve} it?",
|
||||||
student_index=1,
|
student_index=1,
|
||||||
target_mode="sassy",
|
target_mode="sassy",
|
||||||
added_anims=[morty.change, "thinking"],
|
added_anims=[morty.change, "thinking"],
|
||||||
|
|
@ -242,9 +322,7 @@ class HungerForExactness(TeacherStudentsScene):
|
||||||
ode.to_corner(UL)
|
ode.to_corner(UL)
|
||||||
left_part = ode[:5]
|
left_part = ode[:5]
|
||||||
friction_part = ode[5:11]
|
friction_part = ode[5:11]
|
||||||
right_part = ode[11:]
|
|
||||||
self.add(ode)
|
self.add(ode)
|
||||||
frictionless_group = VGroup(left_part, right_part)
|
|
||||||
|
|
||||||
proposed_solution = TexMobject(
|
proposed_solution = TexMobject(
|
||||||
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
|
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
|
||||||
|
|
@ -376,9 +454,62 @@ class HungerForExactness(TeacherStudentsScene):
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
self.wait(3)
|
self.wait(3)
|
||||||
|
self.change_student_modes("tired", "sad", "concerned_musician")
|
||||||
|
self.wait(4)
|
||||||
|
self.look_at(solution)
|
||||||
|
self.wait(5)
|
||||||
self.play(
|
self.play(
|
||||||
FadeOutAndShift(solution, 2 * LEFT),
|
FadeOutAndShift(solution, 2 * LEFT),
|
||||||
Restore(ode),
|
Restore(ode),
|
||||||
self.get_student_changes(*3 * ["sick"])
|
self.get_student_changes(
|
||||||
|
"sick", "angry", "tired",
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.wait(3)
|
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)
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,8 @@ def get_ode():
|
||||||
tex_config = {
|
tex_config = {
|
||||||
"tex_to_color_map": {
|
"tex_to_color_map": {
|
||||||
"{\\theta}": BLUE,
|
"{\\theta}": BLUE,
|
||||||
"{\\dot\\theta}": YELLOW,
|
"{\\dot\\theta}": RED,
|
||||||
"{\\ddot\\theta}": RED,
|
"{\\ddot\\theta}": YELLOW,
|
||||||
"{t}": WHITE,
|
"{t}": WHITE,
|
||||||
"{\\mu}": WHITE,
|
"{\\mu}": WHITE,
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +35,16 @@ def get_ode():
|
||||||
return 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):
|
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
|
||||||
theta, omega = point[:2]
|
theta, omega = point[:2]
|
||||||
return np.array([
|
return np.array([
|
||||||
|
|
@ -54,3 +64,55 @@ def get_vector_symbol(*texs, **kwargs):
|
||||||
config.update(kwargs)
|
config.update(kwargs)
|
||||||
array = [[tex] for tex in texs]
|
array = [[tex] for tex in texs]
|
||||||
return Matrix(array, **config)
|
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
|
|
@ -37,6 +37,40 @@ class SmallAngleApproximationTex(Scene):
|
||||||
|
|
||||||
class StrogatzQuote(Scene):
|
class StrogatzQuote(Scene):
|
||||||
def construct(self):
|
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"
|
law_words = "laws of physics"
|
||||||
language_words = "language of differential equations"
|
language_words = "language of differential equations"
|
||||||
author = "-Steven Strogatz"
|
author = "-Steven Strogatz"
|
||||||
|
|
@ -51,47 +85,342 @@ class StrogatzQuote(Scene):
|
||||||
arg_separator=" ",
|
arg_separator=" ",
|
||||||
substrings_to_isolate=[law_words, language_words, author]
|
substrings_to_isolate=[law_words, language_words, author]
|
||||||
)
|
)
|
||||||
law_part = quote.get_part_by_tex(law_words)
|
quote.law_part = quote.get_part_by_tex(law_words)
|
||||||
language_part = quote.get_part_by_tex(language_words)
|
quote.language_part = quote.get_part_by_tex(language_words)
|
||||||
author_part = quote.get_part_by_tex(author)
|
quote.author_part = quote.get_part_by_tex(author)
|
||||||
quote.set_width(12)
|
quote.set_width(12)
|
||||||
quote.to_edge(UP)
|
quote.to_edge(UP)
|
||||||
quote[-2].shift(SMALL_BUFF * LEFT)
|
quote[-2].shift(SMALL_BUFF * LEFT)
|
||||||
author_part.shift(RIGHT + 0.5 * DOWN)
|
quote.author_part.shift(RIGHT + 0.5 * DOWN)
|
||||||
author_part.scale(1.2, about_edge=UL)
|
quote.author_part.scale(1.2, about_edge=UL)
|
||||||
|
|
||||||
movers = VGroup(*quote[:-1].family_members_with_points())
|
return quote
|
||||||
for mover in movers:
|
|
||||||
mover.save_state()
|
|
||||||
disc = Circle(radius=0.05)
|
class WriteInRadians(Scene):
|
||||||
disc.set_stroke(width=0)
|
def construct(self):
|
||||||
disc.set_fill(BLACK, 0)
|
words = TextMobject("In radians")
|
||||||
disc.move_to(mover)
|
words.set_color(YELLOW)
|
||||||
mover.become(disc)
|
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(
|
self.play(
|
||||||
FadeInFrom(author_part, LEFT),
|
FadeInFrom(label, UP),
|
||||||
LaggedStartMap(
|
GrowArrow(arrow)
|
||||||
# FadeInFromLarge,
|
)
|
||||||
# quote[:-1].family_members_with_points(),
|
self.wait()
|
||||||
Restore, movers,
|
|
||||||
lag_ratio=0.005,
|
|
||||||
run_time=2,
|
class ReallyHardToSolve(Scene):
|
||||||
)
|
def construct(self):
|
||||||
# FadeInFromDown(quote[:-1]),
|
words = TextMobject(
|
||||||
# lag_ratio=0.01,
|
"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.wait()
|
||||||
self.play(
|
self.play(
|
||||||
Write(law_part.copy().set_color(YELLOW)),
|
FadeInFrom(c_word, UL),
|
||||||
run_time=1,
|
GrowArrow(arrows[2]),
|
||||||
|
FadeInFrom(computation, UP)
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(
|
self.play(
|
||||||
Write(language_part.copy().set_color(BLUE)),
|
FadeInFrom(u_word, DL),
|
||||||
run_time=1.5,
|
GrowArrow(arrows[1]),
|
||||||
|
FadeInFromDown(graph)
|
||||||
)
|
)
|
||||||
self.wait(2)
|
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):
|
class SetAsideSeekingSolution(Scene):
|
||||||
def construct(self):
|
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):
|
class ThreeBodySymbols(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.init_coord_groups()
|
self.init_coord_groups()
|
||||||
|
|
@ -260,3 +597,259 @@ class ThreeBodySymbols(Scene):
|
||||||
rate_func=linear,
|
rate_func=linear,
|
||||||
)
|
)
|
||||||
self.play(FadeOut(coord_copies))
|
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",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
||||||
518
active_projects/ode/part2/fourier_series.py
Normal file
518
active_projects/ode/part2/fourier_series.py
Normal 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)
|
||||||
6
active_projects/ode/part2/heat_equation.py
Normal file
6
active_projects/ode/part2/heat_equation.py
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
|
|
||||||
|
class NewSceneName(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
22
active_projects/ode/part2/staging.py
Normal file
22
active_projects/ode/part2/staging.py
Normal 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)
|
||||||
63
active_projects/ode/solve_pendulum_ode_sample_code.py
Normal file
63
active_projects/ode/solve_pendulum_ode_sample_code.py
Normal 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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -151,8 +151,11 @@ class LaggedStartMap(LaggedStart):
|
||||||
args_list.append(arg_creator(submob))
|
args_list.append(arg_creator(submob))
|
||||||
else:
|
else:
|
||||||
args_list.append((submob,))
|
args_list.append((submob,))
|
||||||
|
anim_kwargs = dict(kwargs)
|
||||||
|
if "lag_ratio" in anim_kwargs:
|
||||||
|
anim_kwargs.pop("lag_ratio")
|
||||||
animations = [
|
animations = [
|
||||||
AnimationClass(*args, **kwargs)
|
AnimationClass(*args, **anim_kwargs)
|
||||||
for args in args_list
|
for args in args_list
|
||||||
]
|
]
|
||||||
super().__init__(*animations, **kwargs)
|
super().__init__(*animations, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ class PatreonThanks(Scene):
|
||||||
patreon_logo = PatreonLogo()
|
patreon_logo = PatreonLogo()
|
||||||
patreon_logo.to_edge(UP)
|
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
|
num_groups = float(len(patrons)) / self.max_patron_group_size
|
||||||
proportion_range = np.linspace(0, 1, num_groups + 1)
|
proportion_range = np.linspace(0, 1, num_groups + 1)
|
||||||
indices = (len(patrons) * proportion_range).astype('int')
|
indices = (len(patrons) * proportion_range).astype('int')
|
||||||
|
|
@ -213,7 +213,14 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
||||||
underline.next_to(thanks, DOWN, SMALL_BUFF)
|
underline.next_to(thanks, DOWN, SMALL_BUFF)
|
||||||
thanks.add(underline)
|
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)
|
patrons.scale(self.patron_scale_val)
|
||||||
for patron in patrons:
|
for patron in patrons:
|
||||||
if patron.get_width() > self.max_patron_width:
|
if patron.get_width() > self.max_patron_width:
|
||||||
|
|
@ -252,6 +259,11 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
||||||
self.add(columns, black_rect, line, thanks)
|
self.add(columns, black_rect, line, thanks)
|
||||||
self.wait(wait_time)
|
self.wait(wait_time)
|
||||||
|
|
||||||
|
def modify_patron_name(self, name):
|
||||||
|
if name is "RedAgent14":
|
||||||
|
return "Brian Shepetofsky"
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
class LogoGenerationTemplate(MovingCameraScene):
|
class LogoGenerationTemplate(MovingCameraScene):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,7 @@ class PiCreatureScene(Scene):
|
||||||
continue
|
continue
|
||||||
anims_with_pi_creature = [anim for anim in animations if pi_creature in anim.mobject.get_family()]
|
anims_with_pi_creature = [anim for anim in animations if pi_creature in anim.mobject.get_family()]
|
||||||
for anim in anims_with_pi_creature:
|
for anim in anims_with_pi_creature:
|
||||||
|
continue # TODO, this is broken
|
||||||
if isinstance(anim, Transform):
|
if isinstance(anim, Transform):
|
||||||
index = anim.mobject.get_family().index(pi_creature)
|
index = anim.mobject.get_family().index(pi_creature)
|
||||||
target_family = anim.target_mobject.get_family()
|
target_family = anim.target_mobject.get_family()
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ class AnimatedBoundary(VGroup):
|
||||||
"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
||||||
"max_stroke_width": 3,
|
"max_stroke_width": 3,
|
||||||
"cycle_rate": 0.5,
|
"cycle_rate": 0.5,
|
||||||
|
"back_and_forth": True,
|
||||||
|
"draw_rate_func": smooth,
|
||||||
|
"fade_rate_func": smooth,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, vmobject, **kwargs):
|
def __init__(self, vmobject, **kwargs):
|
||||||
|
|
@ -22,9 +25,11 @@ class AnimatedBoundary(VGroup):
|
||||||
]
|
]
|
||||||
self.add(*self.boundary_copies)
|
self.add(*self.boundary_copies)
|
||||||
self.total_time = 0
|
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
|
# Not actual time, but something which passes at
|
||||||
# an altered rate to make the implementation below
|
# an altered rate to make the implementation below
|
||||||
# cleaner
|
# cleaner
|
||||||
|
|
@ -35,12 +40,14 @@ class AnimatedBoundary(VGroup):
|
||||||
vmobject = self.vmobject
|
vmobject = self.vmobject
|
||||||
|
|
||||||
index = int(time % len(colors))
|
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:
|
if self.back_and_forth and int(time) % 1 == 0:
|
||||||
bounds = (0, alpha)
|
bounds = (1 - draw_alpha, 1)
|
||||||
else:
|
else:
|
||||||
bounds = (1 - alpha, 1)
|
bounds = (0, draw_alpha)
|
||||||
self.full_family_become_partial(growing, vmobject, *bounds)
|
self.full_family_become_partial(growing, vmobject, *bounds)
|
||||||
growing.set_stroke(colors[index], width=msw)
|
growing.set_stroke(colors[index], width=msw)
|
||||||
|
|
||||||
|
|
@ -48,7 +55,7 @@ class AnimatedBoundary(VGroup):
|
||||||
self.full_family_become_partial(fading, vmobject, 0, 1)
|
self.full_family_become_partial(fading, vmobject, 0, 1)
|
||||||
fading.set_stroke(
|
fading.set_stroke(
|
||||||
color=colors[index - 1],
|
color=colors[index - 1],
|
||||||
width=(1 - alpha) * msw
|
width=(1 - fade_alpha) * msw
|
||||||
)
|
)
|
||||||
|
|
||||||
self.total_time += dt
|
self.total_time += dt
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class CoordinateSystem():
|
||||||
)[0],
|
)[0],
|
||||||
target=x,
|
target=x,
|
||||||
lower_bound=self.x_min,
|
lower_bound=self.x_min,
|
||||||
uplper_bound=self.x_max,
|
upper_bound=self.x_max,
|
||||||
)
|
)
|
||||||
if alpha is not None:
|
if alpha is not None:
|
||||||
return graph.point_from_proportion(alpha)
|
return graph.point_from_proportion(alpha)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ def always(method, *args, **kwargs):
|
||||||
|
|
||||||
def f_always(method, *arg_generators, **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
|
of taking in args, it takes in functions which ouput
|
||||||
the relevant arguments.
|
the relevant arguments.
|
||||||
"""
|
"""
|
||||||
|
|
@ -77,16 +77,15 @@ def turn_animation_into_updater(animation, cycle=False, **kwargs):
|
||||||
|
|
||||||
def update(m, dt):
|
def update(m, dt):
|
||||||
run_time = animation.get_run_time()
|
run_time = animation.get_run_time()
|
||||||
alpha = np.clip(
|
time_ratio = animation.total_time / run_time
|
||||||
animation.total_time / run_time,
|
|
||||||
0, 1,
|
|
||||||
)
|
|
||||||
if cycle:
|
if cycle:
|
||||||
animation.total_time = animation.total_time % run_time
|
alpha = time_ratio % 1
|
||||||
elif alpha >= 1:
|
else:
|
||||||
animation.finish()
|
alpha = np.clip(time_ratio, 0, 1)
|
||||||
m.remove_updater(update)
|
if alpha >= 1:
|
||||||
return
|
animation.finish()
|
||||||
|
m.remove_updater(update)
|
||||||
|
return
|
||||||
animation.interpolate(alpha)
|
animation.interpolate(alpha)
|
||||||
animation.update_mobjects(dt)
|
animation.update_mobjects(dt)
|
||||||
animation.total_time += dt
|
animation.total_time += dt
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,10 @@ class NumberLine(Line):
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_tick_numbers(self):
|
def get_tick_numbers(self):
|
||||||
|
u = -1 if self.include_tip else 1
|
||||||
return np.arange(
|
return np.arange(
|
||||||
self.leftmost_tick,
|
self.leftmost_tick,
|
||||||
self.x_max + self.tick_frequency / 2,
|
self.x_max + u * self.tick_frequency / 2,
|
||||||
self.tick_frequency
|
self.tick_frequency
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -393,11 +393,13 @@ class ClockPassesTime(Animation):
|
||||||
radians=hour_radians,
|
radians=hour_radians,
|
||||||
**rot_kwargs
|
**rot_kwargs
|
||||||
)
|
)
|
||||||
|
self.hour_rotation.begin()
|
||||||
self.minute_rotation = Rotating(
|
self.minute_rotation = Rotating(
|
||||||
clock.minute_hand,
|
clock.minute_hand,
|
||||||
radians=12 * hour_radians,
|
radians=12 * hour_radians,
|
||||||
**rot_kwargs
|
**rot_kwargs
|
||||||
)
|
)
|
||||||
|
self.minute_rotation.begin()
|
||||||
Animation.__init__(self, clock, **kwargs)
|
Animation.__init__(self, clock, **kwargs)
|
||||||
|
|
||||||
def interpolate_mobject(self, alpha):
|
def interpolate_mobject(self, alpha):
|
||||||
|
|
|
||||||
|
|
@ -2817,18 +2817,16 @@ class ShowTwoPopulations(Scene):
|
||||||
num_foxes = Integer(10)
|
num_foxes = Integer(10)
|
||||||
num_foxes.scale(self.count_word_scale_val)
|
num_foxes.scale(self.count_word_scale_val)
|
||||||
num_foxes.next_to(labels[0], RIGHT)
|
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 = Integer(10)
|
||||||
num_rabbits.scale(self.count_word_scale_val)
|
num_rabbits.scale(self.count_word_scale_val)
|
||||||
num_rabbits.next_to(labels[1], RIGHT)
|
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.add_updater(lambda d: d.set_value(get_num_foxes()))
|
||||||
num_foxes, lambda a: get_num_foxes()
|
num_rabbits.add_updater(lambda d: d.set_value(get_num_rabbits()))
|
||||||
))
|
|
||||||
self.add(ContinualChangingDecimal(
|
self.add(num_foxes, num_rabbits)
|
||||||
num_rabbits, lambda a: get_num_rabbits()
|
|
||||||
))
|
|
||||||
|
|
||||||
for count in num_foxes, num_rabbits:
|
for count in num_foxes, num_rabbits:
|
||||||
self.add(Mobject.add_updater(
|
self.add(Mobject.add_updater(
|
||||||
|
|
@ -2853,13 +2851,13 @@ class ShowTwoPopulations(Scene):
|
||||||
height=self.animal_height,
|
height=self.animal_height,
|
||||||
fill_color=color,
|
fill_color=color,
|
||||||
)
|
)
|
||||||
for submob in result.family_members_with_points():
|
# for submob in result.family_members_with_points():
|
||||||
if submob.is_subpath:
|
# if submob.is_subpath:
|
||||||
submob.is_subpath = False
|
# submob.is_subpath = False
|
||||||
submob.set_fill(
|
# submob.set_fill(
|
||||||
interpolate_color(color, BLACK, 0.8),
|
# interpolate_color(color, BLACK, 0.8),
|
||||||
opacity=1
|
# opacity=1
|
||||||
)
|
# )
|
||||||
x_shift, y_shift = [
|
x_shift, y_shift = [
|
||||||
(2 * random.random() - 1) * max_val
|
(2 * random.random() - 1) * max_val
|
||||||
for max_val in [
|
for max_val in [
|
||||||
|
|
@ -3021,25 +3019,7 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
|
||||||
self.pop_sizes_updates = pop_sizes_updates
|
self.pop_sizes_updates = pop_sizes_updates
|
||||||
|
|
||||||
def write_differential_equations(self):
|
def write_differential_equations(self):
|
||||||
variables = ["X", "YY"]
|
equations = self.get_equations()
|
||||||
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.shift(2 * DOWN)
|
equations.shift(2 * DOWN)
|
||||||
rect = SurroundingRectangle(equations, color=YELLOW)
|
rect = SurroundingRectangle(equations, color=YELLOW)
|
||||||
rect.set_fill(BLACK, 0.8)
|
rect.set_fill(BLACK, 0.8)
|
||||||
|
|
@ -3183,6 +3163,28 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
|
||||||
)
|
)
|
||||||
self.wait(self.flow_time)
|
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):
|
class PhaseFlowWords(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ def stage_scenes(module_name):
|
||||||
# }
|
# }
|
||||||
# TODO, fix this
|
# TODO, fix this
|
||||||
animation_dir = os.path.join(
|
animation_dir = os.path.join(
|
||||||
VIDEO_DIR, "clacks_solution2", "1440p60"
|
VIDEO_DIR, "ode", "part1", "1440p60"
|
||||||
)
|
)
|
||||||
#
|
#
|
||||||
files = os.listdir(animation_dir)
|
files = os.listdir(animation_dir)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue