mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 08: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"
|
||||
ALL_SCENE_CLASSES = [
|
||||
WhenChangeIsEasier,
|
||||
VectorFieldTest,
|
||||
IntroducePendulum,
|
||||
MultiplePendulumsOverlayed,
|
||||
PeriodFormula,
|
||||
FormulasAreLies,
|
||||
MediumAnglePendulum,
|
||||
MediumHighAnglePendulum,
|
||||
|
|
@ -18,21 +21,57 @@ ALL_SCENE_CLASSES = [
|
|||
VeryLowAnglePendulum,
|
||||
FormulasAreLies,
|
||||
TourOfDifferentialEquations,
|
||||
WherePendulumLeads,
|
||||
LongDoublePendulum,
|
||||
# FollowThisThread,
|
||||
StrogatzQuote,
|
||||
ShowHorizontalDashedLine,
|
||||
RabbitFoxPopulations,
|
||||
RabbitFoxEquation,
|
||||
# Something...
|
||||
ShowSimpleTrajectory,
|
||||
SimpleProjectileEquation,
|
||||
SimpleProjectileEquationVGraphFreedom,
|
||||
ShowGravityAcceleration,
|
||||
UniversalGravityLawSymbols,
|
||||
ExampleTypicalODE,
|
||||
AnalyzePendulumForce,
|
||||
ShowSineValues,
|
||||
BuildUpEquation,
|
||||
AirResistanceBrace,
|
||||
ShowDerivativeVideo,
|
||||
SubtleAirCurrents,
|
||||
SimpleDampenedPendulum,
|
||||
DefineODE,
|
||||
SecondOrderEquationExample,
|
||||
ODEvsPDEinFrames,
|
||||
ProveTeacherWrong,
|
||||
SetAsideSeekingSolution,
|
||||
ReferencePiCollisionStateSpaces,
|
||||
#
|
||||
WriteInRadians,
|
||||
XEqLThetaToCorner,
|
||||
ComingUp,
|
||||
InputLabel,
|
||||
SoWhatIsThetaThen,
|
||||
ReallyHardToSolve,
|
||||
ReasonForSolution,
|
||||
PhysicistPhaseSpace,
|
||||
GleickQuote,
|
||||
SpectrumOfStartingStates,
|
||||
WritePhaseFlow,
|
||||
AskAboutStability,
|
||||
LoveExample,
|
||||
PassageOfTime,
|
||||
LovePhaseSpace,
|
||||
ComparePhysicsToLove,
|
||||
FramesComparingPhysicsToLove,
|
||||
SetupToTakingManyTinySteps,
|
||||
ShowClutterPrevention,
|
||||
# VisualizeHeightSlopeCurvature,
|
||||
VisualizeStates,
|
||||
ReferencePiCollisionStateSpaces,
|
||||
IntroduceVectorField,
|
||||
XComponentArrows,
|
||||
BreakingSecondOrderIntoTwoFirstOrder,
|
||||
ShowPendulumPhaseFlow,
|
||||
ShowHighVelocityCase,
|
||||
|
|
@ -42,10 +81,27 @@ ALL_SCENE_CLASSES = [
|
|||
LorenzVectorField,
|
||||
ThreeBodiesInSpace,
|
||||
AltThreeBodiesInSpace,
|
||||
TwoBodiesInSpace,
|
||||
TwoBodiesWithZPart,
|
||||
ThreeBodyTitle,
|
||||
ThreeBodySymbols,
|
||||
#
|
||||
HighAmplitudePendulum,
|
||||
WritePhaseSpace,
|
||||
#
|
||||
AskAboutActuallySolving,
|
||||
WriteODESolvingCode,
|
||||
TakeManyTinySteps,
|
||||
ManyStepsFromDifferentStartingPoints,
|
||||
InaccurateComputation,
|
||||
HungerForExactness,
|
||||
ShowRect,
|
||||
ShowSquare,
|
||||
JumpToThisPoint,
|
||||
ThreeBodyEquation,
|
||||
ItGetsWorse,
|
||||
ChaosTitle,
|
||||
RevisitQuote,
|
||||
EndScreen,
|
||||
Thumbnail,
|
||||
]
|
||||
|
|
|
|||
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.add(theta_label)
|
||||
|
||||
self.y_axis_label = theta_label
|
||||
self.x_axis_label = t_label
|
||||
|
||||
x_axis.add_numbers()
|
||||
y_axis.add(self.get_y_axis_coordinates(y_axis))
|
||||
|
||||
|
|
@ -357,6 +360,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
"length": 3,
|
||||
"top_point": 4 * RIGHT,
|
||||
"weight_diameter": 0.35,
|
||||
"gravity": 20,
|
||||
},
|
||||
"theta_vs_t_axes_config": {
|
||||
"y_max": PI / 4,
|
||||
|
|
@ -366,6 +370,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
"unit_size": 2,
|
||||
"tip_length": 0.3,
|
||||
},
|
||||
"x_max": 12,
|
||||
"number_line_config": {
|
||||
"stroke_width": 2,
|
||||
}
|
||||
|
|
@ -378,12 +383,13 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
|
||||
def construct(self):
|
||||
self.add_pendulum()
|
||||
self.label_pi_creatures()
|
||||
# self.label_pi_creatures()
|
||||
self.label_pendulum()
|
||||
self.add_graph()
|
||||
self.label_function()
|
||||
self.show_graph_period()
|
||||
self.show_length_and_gravity()
|
||||
self.tweak_length_and_gravity()
|
||||
# self.tweak_length_and_gravity()
|
||||
|
||||
def create_pi_creatures(self):
|
||||
randy = Randolph(color=BLUE_C)
|
||||
|
|
@ -437,7 +443,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
GrowArrow(morty_label.arrow),
|
||||
morty.change, "raise_right_hand",
|
||||
)
|
||||
self.wait()
|
||||
self.wait(2)
|
||||
|
||||
def label_pendulum(self):
|
||||
pendulum = self.pendulum
|
||||
|
|
@ -446,18 +452,18 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
rect = SurroundingRectangle(label, buff=0.5 * SMALL_BUFF)
|
||||
rect.add_updater(lambda r: r.move_to(label))
|
||||
|
||||
self.add(rect)
|
||||
for pi in randy, morty:
|
||||
pi.add_updater(
|
||||
lambda m: m.look_at(pendulum.weight)
|
||||
)
|
||||
|
||||
self.play(randy.change, "pondering")
|
||||
self.play(morty.change, "pondering")
|
||||
self.wait(3)
|
||||
randy.clear_updaters()
|
||||
morty.clear_updaters()
|
||||
self.play(
|
||||
ShowCreationThenFadeOut(rect),
|
||||
ShowCreationThenDestruction(
|
||||
label.copy().set_style(
|
||||
fill_opacity=0,
|
||||
stroke_color=PINK,
|
||||
stroke_width=2,
|
||||
)
|
||||
),
|
||||
randy.change, "pondering",
|
||||
morty.change, "pondering",
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
|
@ -467,7 +473,10 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
axes.to_corner(UL)
|
||||
|
||||
self.play(
|
||||
Restore(self.camera_frame),
|
||||
Restore(
|
||||
self.camera_frame,
|
||||
rate_func=squish_rate_func(smooth, 0, 0.9),
|
||||
),
|
||||
DrawBorderThenFill(
|
||||
axes,
|
||||
rate_func=squish_rate_func(smooth, 0.5, 1),
|
||||
|
|
@ -481,10 +490,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
),
|
||||
run_time=3,
|
||||
)
|
||||
self.wait(2)
|
||||
self.graph = axes.get_live_drawn_graph(self.pendulum)
|
||||
|
||||
self.wait(1.5)
|
||||
self.graph = axes.get_live_drawn_graph(self.pendulum)
|
||||
self.add(self.graph)
|
||||
|
||||
def label_function(self):
|
||||
hm_word = TextMobject("Simple harmonic motion")
|
||||
hm_word.scale(1.25)
|
||||
hm_word.to_edge(UP)
|
||||
|
||||
formula = TexMobject(
|
||||
"=\\theta_0 \\cos(\\sqrt{g / L} t)"
|
||||
)
|
||||
formula.next_to(
|
||||
self.axes.y_axis_label, RIGHT, SMALL_BUFF
|
||||
)
|
||||
formula.set_stroke(width=0, background=True)
|
||||
|
||||
self.play(FadeInFrom(hm_word, DOWN))
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(formula),
|
||||
hm_word.to_corner, UR
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
def show_graph_period(self):
|
||||
|
|
@ -503,13 +532,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
line.shift(SMALL_BUFF * RIGHT)
|
||||
brace = Brace(line, UP, buff=SMALL_BUFF)
|
||||
brace.add_to_back(brace.copy().set_style(BLACK, 10))
|
||||
formula = TexMobject(
|
||||
"\\sqrt{\\,", "2\\pi", "L", "/", "g", "}",
|
||||
tex_to_color_map={
|
||||
"L": BLUE,
|
||||
"g": YELLOW,
|
||||
}
|
||||
)
|
||||
formula = get_period_formula()
|
||||
formula.next_to(brace, UP, SMALL_BUFF)
|
||||
|
||||
self.period_formula = formula
|
||||
|
|
@ -537,29 +560,22 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
self.pendulum,
|
||||
length_multiple=0.5 / 9.8,
|
||||
)
|
||||
down_vectors = self.get_down_vectors()
|
||||
down_vectors.set_color(YELLOW)
|
||||
down_vectors.set_opacity(0.5)
|
||||
|
||||
self.play(ShowCreationThenDestructionAround(L))
|
||||
dot = Dot(fill_opacity=0.25)
|
||||
dot.move_to(L)
|
||||
self.play(
|
||||
ShowCreationThenDestructionAround(L),
|
||||
ShowCreation(new_rod),
|
||||
dot.move_to, new_rod,
|
||||
dot.fade, 1,
|
||||
)
|
||||
self.remove(dot)
|
||||
self.play(FadeOut(new_rod))
|
||||
self.wait()
|
||||
|
||||
self.play(ShowCreationThenDestructionAround(g))
|
||||
dot.move_to(g)
|
||||
dot.set_fill(opacity=0.5)
|
||||
self.play(
|
||||
ShowCreationThenDestructionAround(g),
|
||||
GrowArrow(g_vect),
|
||||
dot.move_to, g_vect,
|
||||
dot.fade, 1,
|
||||
)
|
||||
self.remove(dot)
|
||||
self.wait(2)
|
||||
self.play(self.get_down_vectors_animation(down_vectors))
|
||||
self.wait(6)
|
||||
|
||||
self.gravity_vector = g_vect
|
||||
|
||||
|
|
@ -593,15 +609,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
# new_pendulum_config["initial_theta"] = pendulum.get_theta()
|
||||
new_pendulum = Pendulum(**new_pendulum_config)
|
||||
|
||||
down_vectors = VGroup(*[
|
||||
Vector(0.5 * DOWN)
|
||||
for x in range(10 * 150)
|
||||
])
|
||||
down_vectors.arrange_in_grid(10, 150, buff=MED_SMALL_BUFF)
|
||||
down_vectors.set_color_by_gradient(BLUE, RED)
|
||||
# for vect in down_vectors:
|
||||
# vect.shift(0.1 * np.random.random(3))
|
||||
down_vectors.to_edge(RIGHT)
|
||||
down_vectors = self.get_down_vectors()
|
||||
|
||||
self.play(randy.change, "happy")
|
||||
self.play(
|
||||
|
|
@ -624,10 +632,7 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
g_vect.scale(2)
|
||||
self.play(
|
||||
FadeOut(graph2),
|
||||
LaggedStart(*[
|
||||
GrowArrow(v, rate_func=there_and_back)
|
||||
for v in down_vectors
|
||||
], lag_ratio=0.0005, run_time=2, remover=True)
|
||||
self.get_down_vectors_animation(down_vectors)
|
||||
)
|
||||
self.play(
|
||||
FadeIn(graph3),
|
||||
|
|
@ -635,6 +640,30 @@ class IntroducePendulum(PiCreatureScene, MovingCameraScene):
|
|||
)
|
||||
self.wait(6)
|
||||
|
||||
#
|
||||
def get_down_vectors(self):
|
||||
down_vectors = VGroup(*[
|
||||
Vector(0.5 * DOWN)
|
||||
for x in range(10 * 150)
|
||||
])
|
||||
down_vectors.arrange_in_grid(10, 150, buff=MED_SMALL_BUFF)
|
||||
down_vectors.set_color_by_gradient(BLUE, RED)
|
||||
# for vect in down_vectors:
|
||||
# vect.shift(0.1 * np.random.random(3))
|
||||
down_vectors.to_edge(RIGHT)
|
||||
return down_vectors
|
||||
|
||||
def get_down_vectors_animation(self, down_vectors):
|
||||
return LaggedStart(
|
||||
*[
|
||||
GrowArrow(v, rate_func=there_and_back)
|
||||
for v in down_vectors
|
||||
],
|
||||
lag_ratio=0.0005,
|
||||
run_time=2,
|
||||
remover=True
|
||||
)
|
||||
|
||||
|
||||
class MultiplePendulumsOverlayed(Scene):
|
||||
CONFIG = {
|
||||
|
|
@ -868,6 +897,95 @@ class VeryLowAnglePendulum(LowAnglePendulum):
|
|||
}
|
||||
|
||||
|
||||
class WherePendulumLeads(PiCreatureScene):
|
||||
def construct(self):
|
||||
pendulum = Pendulum(
|
||||
top_point=UP,
|
||||
length=3,
|
||||
gravity=20,
|
||||
)
|
||||
pendulum.start_swinging()
|
||||
|
||||
l_title = TextMobject("Linearization")
|
||||
l_title.scale(1.5)
|
||||
l_title.to_corner(UL)
|
||||
c_title = TextMobject("Chaos")
|
||||
c_title.scale(1.5)
|
||||
c_title.move_to(l_title)
|
||||
c_title.move_to(
|
||||
c_title.get_center() * np.array([-1, 1, 1])
|
||||
)
|
||||
|
||||
get_theta = pendulum.get_theta
|
||||
spring = always_redraw(
|
||||
lambda: ParametricFunction(
|
||||
lambda t: np.array([
|
||||
np.cos(TAU * t) + (1.4 + get_theta()) * t,
|
||||
np.sin(TAU * t) - 0.5,
|
||||
0,
|
||||
]),
|
||||
t_min=-0.5,
|
||||
t_max=7,
|
||||
color=GREY,
|
||||
sheen_factor=1,
|
||||
sheen_direction=UL,
|
||||
).scale(0.2).to_edge(LEFT, buff=0)
|
||||
)
|
||||
spring_rect = SurroundingRectangle(
|
||||
spring, buff=MED_LARGE_BUFF,
|
||||
stroke_width=0,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=0,
|
||||
)
|
||||
|
||||
weight = Dot(radius=0.25)
|
||||
weight.add_updater(lambda m: m.move_to(
|
||||
spring.points[-1]
|
||||
))
|
||||
weight.set_color(BLUE)
|
||||
weight.set_sheen(1, UL)
|
||||
spring_system = VGroup(spring, weight)
|
||||
|
||||
linear_formula = TexMobject(
|
||||
"\\frac{d \\vec{\\textbf{x}}}{dt}="
|
||||
"A\\vec{\\textbf{x}}"
|
||||
)
|
||||
linear_formula.next_to(spring, UP, LARGE_BUFF)
|
||||
linear_formula.match_x(l_title)
|
||||
|
||||
randy = self.pi_creature
|
||||
randy.set_height(2)
|
||||
randy.center()
|
||||
randy.to_edge(DOWN)
|
||||
randy.shift(3 * LEFT)
|
||||
q_marks = TexMobject("???")
|
||||
q_marks.next_to(randy, UP)
|
||||
|
||||
self.add(pendulum, randy)
|
||||
self.play(
|
||||
randy.change, "pondering", pendulum,
|
||||
FadeInFromDown(q_marks, lag_ratio=0.3)
|
||||
)
|
||||
self.play(randy.look_at, pendulum)
|
||||
self.wait(5)
|
||||
self.play(
|
||||
Animation(VectorizedPoint(pendulum.get_top())),
|
||||
FadeOutAndShift(q_marks, UP, lag_ratio=0.3),
|
||||
)
|
||||
self.add(spring_system)
|
||||
self.play(
|
||||
FadeOut(spring_rect),
|
||||
FadeInFrom(linear_formula, UP),
|
||||
FadeInFromDown(l_title),
|
||||
)
|
||||
self.play(FadeInFromDown(c_title))
|
||||
self.wait(8)
|
||||
|
||||
|
||||
class LongDoublePendulum(ExternallyAnimatedScene):
|
||||
pass
|
||||
|
||||
|
||||
class AnalyzePendulumForce(MovingCameraScene):
|
||||
CONFIG = {
|
||||
"pendulum_config": {
|
||||
|
|
@ -885,19 +1003,20 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
|
||||
def construct(self):
|
||||
self.add_pendulum()
|
||||
self.show_arc_length()
|
||||
self.add_g_vect()
|
||||
self.show_constraint()
|
||||
self.break_g_vect_into_components()
|
||||
self.show_gsin_formula()
|
||||
self.show_acceleration_at_different_angles()
|
||||
self.show_angle_geometry()
|
||||
self.ask_about_what_to_do()
|
||||
self.show_gsin_formula()
|
||||
self.show_sign()
|
||||
self.show_acceleration_formula()
|
||||
# self.ask_about_what_to_do()
|
||||
|
||||
self.emphasize_theta()
|
||||
self.show_arc_length()
|
||||
self.show_angular_velocity()
|
||||
self.show_angular_acceleration()
|
||||
self.circle_g_sin_formula()
|
||||
# self.emphasize_theta()
|
||||
# self.show_angular_velocity()
|
||||
# self.show_angular_acceleration()
|
||||
# self.circle_g_sin_formula()
|
||||
|
||||
def add_pendulum(self):
|
||||
pendulum = Pendulum(**self.pendulum_config)
|
||||
|
|
@ -910,6 +1029,97 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
self.pendulum = pendulum
|
||||
self.theta_tracker = theta_tracker
|
||||
|
||||
def show_arc_length(self):
|
||||
pendulum = self.pendulum
|
||||
angle = pendulum.get_theta()
|
||||
height = pendulum.length
|
||||
top = pendulum.get_fixed_point()
|
||||
|
||||
line = Line(UP, DOWN)
|
||||
line.set_height(height)
|
||||
line.move_to(top, UP)
|
||||
arc = always_redraw(lambda: Arc(
|
||||
start_angle=-90 * DEGREES,
|
||||
angle=pendulum.get_theta(),
|
||||
arc_center=pendulum.get_fixed_point(),
|
||||
radius=pendulum.length,
|
||||
stroke_color=GREEN,
|
||||
))
|
||||
|
||||
brace = Brace(Line(ORIGIN, 5 * UP), RIGHT)
|
||||
brace.point = VectorizedPoint(brace.get_right())
|
||||
brace.add(brace.point)
|
||||
brace.set_height(angle)
|
||||
brace.move_to(ORIGIN, DL)
|
||||
brace.apply_complex_function(np.exp)
|
||||
brace.scale(height)
|
||||
brace.rotate(-90 * DEGREES)
|
||||
brace.move_to(arc)
|
||||
brace.shift(MED_SMALL_BUFF * normalize(
|
||||
arc.point_from_proportion(0.5) - top
|
||||
))
|
||||
x_sym = TexMobject("x")
|
||||
x_sym.set_color(GREEN)
|
||||
x_sym.next_to(brace.point, DR, buff=SMALL_BUFF)
|
||||
|
||||
rhs = TexMobject("=", "L", "\\theta")
|
||||
rhs.set_color_by_tex("\\theta", BLUE)
|
||||
rhs.next_to(x_sym, RIGHT)
|
||||
rhs.shift(0.7 * SMALL_BUFF * UP)
|
||||
line_L = TexMobject("L")
|
||||
line_L.next_to(
|
||||
pendulum.rod.get_center(), UR, SMALL_BUFF,
|
||||
)
|
||||
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
Rotate(line, angle, about_point=top),
|
||||
UpdateFromAlphaFunc(
|
||||
line, lambda m, a: m.set_stroke(
|
||||
width=2 * there_and_back(a)
|
||||
)
|
||||
),
|
||||
GrowFromPoint(
|
||||
brace, line.get_bottom(),
|
||||
path_arc=angle
|
||||
),
|
||||
)
|
||||
self.play(FadeInFrom(x_sym, UP))
|
||||
self.wait()
|
||||
|
||||
# Show equation
|
||||
line.set_stroke(BLUE, 5)
|
||||
self.play(
|
||||
ShowCreationThenFadeOut(line),
|
||||
FadeInFromDown(line_L)
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
line_L, rhs.get_part_by_tex("L")
|
||||
),
|
||||
Write(rhs.get_part_by_tex("="))
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
pendulum.theta_label,
|
||||
rhs.get_parts_by_tex("\\theta"),
|
||||
)
|
||||
)
|
||||
self.add(rhs)
|
||||
|
||||
x_eq = VGroup(x_sym, rhs)
|
||||
|
||||
self.play(
|
||||
FadeOut(brace),
|
||||
x_eq.rotate, angle / 2,
|
||||
x_eq.next_to, arc.point_from_proportion(0.5),
|
||||
UL, {"buff": -MED_SMALL_BUFF}
|
||||
)
|
||||
|
||||
self.x_eq = x_eq
|
||||
self.arc = arc
|
||||
self.line_L = line_L
|
||||
|
||||
def add_g_vect(self):
|
||||
pendulum = self.pendulum
|
||||
|
||||
|
|
@ -927,15 +1137,10 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
GrowArrow(g_vect),
|
||||
FadeInFrom(g_word, UP, lag_ratio=0.1),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def show_constraint(self):
|
||||
pendulum = self.pendulum
|
||||
weight = pendulum.weight
|
||||
p = weight.get_center()
|
||||
path = CubicBezier([p, p + 3 * DOWN, p + 3 * UP, p])
|
||||
|
||||
self.play(MoveAlongPath(weight, path, run_time=2))
|
||||
self.wait()
|
||||
|
||||
arcs = VGroup()
|
||||
for u in [-1, 2, -1]:
|
||||
|
|
@ -946,7 +1151,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
radius=pendulum.length,
|
||||
arc_center=pendulum.get_fixed_point(),
|
||||
stroke_width=2,
|
||||
stroke_color=GREEN,
|
||||
stroke_color=YELLOW,
|
||||
stroke_opacity=0.5,
|
||||
)
|
||||
self.play(
|
||||
|
|
@ -954,9 +1159,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
ShowCreation(arc)
|
||||
)
|
||||
arcs.add(arc)
|
||||
self.wait()
|
||||
|
||||
self.traced_arcs = arcs
|
||||
self.play(FadeOut(arcs))
|
||||
|
||||
def break_g_vect_into_components(self):
|
||||
g_vect = self.g_vect
|
||||
|
|
@ -983,6 +1186,52 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
self.play(GrowArrow(g_vect.perp))
|
||||
self.wait()
|
||||
|
||||
def show_angle_geometry(self):
|
||||
g_vect = self.g_vect
|
||||
|
||||
arc = Arc(
|
||||
start_angle=90 * DEGREES,
|
||||
angle=self.pendulum.get_theta(),
|
||||
radius=0.5,
|
||||
arc_center=g_vect.get_end(),
|
||||
)
|
||||
q_mark = TexMobject("?")
|
||||
q_mark.next_to(arc.get_center(), UL, SMALL_BUFF)
|
||||
theta_label = TexMobject("\\theta")
|
||||
theta_label.move_to(q_mark)
|
||||
|
||||
self.add(g_vect)
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
Write(q_mark)
|
||||
)
|
||||
self.play(ShowCreationThenFadeAround(q_mark))
|
||||
self.wait()
|
||||
self.play(ShowCreationThenFadeAround(
|
||||
self.pendulum.theta_label
|
||||
))
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
self.pendulum.theta_label,
|
||||
theta_label,
|
||||
),
|
||||
FadeOut(q_mark)
|
||||
)
|
||||
self.wait()
|
||||
self.play(WiggleOutThenIn(g_vect.tangent))
|
||||
self.play(WiggleOutThenIn(
|
||||
Line(
|
||||
*g_vect.get_start_and_end(),
|
||||
buff=0,
|
||||
).add_tip().match_style(g_vect),
|
||||
remover=True
|
||||
))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(arc),
|
||||
FadeOut(theta_label),
|
||||
)
|
||||
|
||||
def show_gsin_formula(self):
|
||||
g_vect = self.g_vect
|
||||
g_word = self.g_word
|
||||
|
|
@ -1037,27 +1286,7 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
self.g_sin_label = g_sin_label
|
||||
self.g_cos_label = g_cos_label
|
||||
|
||||
def show_acceleration_at_different_angles(self):
|
||||
to_fade = VGroup(
|
||||
self.g_cos_label,
|
||||
self.g_vect.perp,
|
||||
)
|
||||
new_comp_line_sytle = {
|
||||
"stroke_width": 0.5,
|
||||
"stroke_opacity": 0.25,
|
||||
}
|
||||
|
||||
self.play(
|
||||
to_fade.set_opacity, 0.25,
|
||||
self.g_vect.component_lines.set_style,
|
||||
new_comp_line_sytle
|
||||
)
|
||||
self.g_vect.component_lines.add_updater(
|
||||
lambda m: m.set_style(**new_comp_line_sytle)
|
||||
)
|
||||
for mob in to_fade:
|
||||
mob.add_updater(lambda m: m.set_opacity(0.25))
|
||||
|
||||
def show_sign(self):
|
||||
get_theta = self.pendulum.get_theta
|
||||
theta_decimal = DecimalNumber(include_sign=True)
|
||||
theta_decimal.add_updater(lambda d: d.set_value(
|
||||
|
|
@ -1070,84 +1299,148 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
GREEN if get_theta() > 0 else RED
|
||||
))
|
||||
|
||||
self.play(
|
||||
FadeInFrom(theta_decimal, UP),
|
||||
FadeOut(self.x_eq),
|
||||
FadeOut(self.line_L),
|
||||
)
|
||||
self.set_theta(-60 * DEGREES, run_time=4)
|
||||
self.set_theta(60 * DEGREES, run_time=4)
|
||||
self.play(
|
||||
FadeOut(theta_decimal),
|
||||
FadeIn(self.x_eq),
|
||||
)
|
||||
|
||||
def show_acceleration_formula(self):
|
||||
x_eq = self.x_eq
|
||||
g_sin_theta = self.g_sin_label
|
||||
|
||||
equation = TexMobject(
|
||||
"a", "=",
|
||||
"\\ddot", "x",
|
||||
"=",
|
||||
"-", "g", "\\sin\\big(", "\\theta", "\\big)",
|
||||
)
|
||||
equation.to_edge(LEFT)
|
||||
|
||||
second_deriv = equation[2:4]
|
||||
x_part = equation.get_part_by_tex("x")
|
||||
x_part.set_color(GREEN)
|
||||
a_eq = equation[:2]
|
||||
eq2 = equation.get_parts_by_tex("=")[1]
|
||||
rhs = equation[5:]
|
||||
|
||||
second_deriv_L_form = TexMobject(
|
||||
"L", "\\ddot", "\\theta"
|
||||
)
|
||||
second_deriv_L_form.move_to(second_deriv, DOWN)
|
||||
eq3 = TexMobject("=")
|
||||
eq3.rotate(90 * DEGREES)
|
||||
eq3.next_to(second_deriv_L_form, UP)
|
||||
|
||||
g_L_frac = TexMobject(
|
||||
"-", "{g", "\\over", "L}"
|
||||
)
|
||||
g_L_frac.move_to(rhs[:2], LEFT)
|
||||
g_L_frac.shift(SMALL_BUFF * UP / 2)
|
||||
|
||||
mu_term = TexMobject(
|
||||
"-\\mu", "\\dot", "\\theta",
|
||||
)
|
||||
mu_term.next_to(g_L_frac, LEFT)
|
||||
mu_term.shift(SMALL_BUFF * UP / 2)
|
||||
|
||||
mu_brace = Brace(mu_term, UP)
|
||||
mu_word = mu_brace.get_text("Air resistance")
|
||||
|
||||
for mob in equation, second_deriv_L_form, mu_term:
|
||||
mob.set_color_by_tex("\\theta", BLUE)
|
||||
|
||||
self.play(
|
||||
TransformFromCopy(x_eq[0], x_part),
|
||||
Write(equation[:3]),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(eq2),
|
||||
TransformFromCopy(g_sin_theta, rhs)
|
||||
)
|
||||
self.wait()
|
||||
#
|
||||
self.show_acceleration_at_different_angles()
|
||||
#
|
||||
self.play(
|
||||
FadeInFromDown(second_deriv_L_form),
|
||||
Write(eq3),
|
||||
second_deriv.next_to, eq3, UP,
|
||||
a_eq.shift, SMALL_BUFF * LEFT,
|
||||
eq2.shift, SMALL_BUFF * RIGHT,
|
||||
rhs.shift, SMALL_BUFF * RIGHT,
|
||||
)
|
||||
self.wait()
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(a_eq),
|
||||
FadeOut(second_deriv),
|
||||
FadeOut(eq3),
|
||||
ReplacementTransform(
|
||||
second_deriv_L_form.get_part_by_tex("L"),
|
||||
g_L_frac.get_part_by_tex("L"),
|
||||
),
|
||||
ReplacementTransform(
|
||||
equation.get_part_by_tex("-"),
|
||||
g_L_frac.get_part_by_tex("-"),
|
||||
),
|
||||
ReplacementTransform(
|
||||
equation.get_part_by_tex("g"),
|
||||
g_L_frac.get_part_by_tex("g"),
|
||||
),
|
||||
Write(g_L_frac.get_part_by_tex("\\over")),
|
||||
rhs[2:].next_to, g_L_frac, RIGHT, {"buff": SMALL_BUFF},
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
GrowFromCenter(mu_term),
|
||||
VGroup(eq2, second_deriv_L_form[1:]).next_to,
|
||||
mu_term, LEFT,
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(mu_brace),
|
||||
FadeInFromDown(mu_word),
|
||||
)
|
||||
|
||||
def show_acceleration_at_different_angles(self):
|
||||
to_fade = VGroup(
|
||||
self.g_cos_label,
|
||||
self.g_vect.perp,
|
||||
)
|
||||
new_comp_line_sytle = {
|
||||
"stroke_width": 0.5,
|
||||
"stroke_opacity": 0.25,
|
||||
}
|
||||
|
||||
self.play(
|
||||
FadeOut(self.x_eq),
|
||||
to_fade.set_opacity, 0.25,
|
||||
self.g_vect.component_lines.set_style,
|
||||
new_comp_line_sytle
|
||||
)
|
||||
self.g_vect.component_lines.add_updater(
|
||||
lambda m: m.set_style(**new_comp_line_sytle)
|
||||
)
|
||||
for mob in to_fade:
|
||||
mob.add_updater(lambda m: m.set_opacity(0.25))
|
||||
|
||||
self.set_theta(0)
|
||||
self.wait(2)
|
||||
self.set_theta(89.9 * DEGREES, run_time=3)
|
||||
self.wait(2)
|
||||
self.set_theta(60 * DEGREES, run_time=2)
|
||||
self.wait()
|
||||
|
||||
self.play(FadeInFrom(theta_decimal, UP))
|
||||
self.set_theta(-60 * DEGREES, run_time=4)
|
||||
self.set_theta(60 * DEGREES, run_time=4)
|
||||
self.play(FadeOut(theta_decimal))
|
||||
|
||||
def show_angle_geometry(self):
|
||||
g_vect = self.g_vect
|
||||
vectors = VGroup(
|
||||
g_vect, g_vect.tangent, g_vect.perp,
|
||||
)
|
||||
g_line = Line(
|
||||
g_vect.get_start(),
|
||||
g_vect.get_end(),
|
||||
stroke_width=1,
|
||||
stroke_color=WHITE,
|
||||
)
|
||||
|
||||
arc = Arc(
|
||||
start_angle=90 * DEGREES,
|
||||
angle=self.pendulum.get_theta(),
|
||||
radius=0.5,
|
||||
arc_center=g_vect.get_end(),
|
||||
)
|
||||
q_mark = TexMobject("?")
|
||||
q_mark.next_to(arc.get_center(), UL, SMALL_BUFF)
|
||||
theta_label = TexMobject("\\theta")
|
||||
theta_label.move_to(q_mark)
|
||||
|
||||
opposite = Line(
|
||||
*g_vect.component_lines[0].get_start_and_end()
|
||||
)
|
||||
adjascent = g_line.copy()
|
||||
opposite.set_stroke(BLUE, 5, opacity=1)
|
||||
adjascent.set_stroke(YELLOW, 5)
|
||||
|
||||
vectors.save_state()
|
||||
vectors.suspend_updating()
|
||||
self.add(g_line, g_vect)
|
||||
self.play(vectors.set_opacity, 0.3)
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
Write(q_mark)
|
||||
)
|
||||
self.play(ShowCreationThenFadeAround(q_mark))
|
||||
self.wait()
|
||||
self.play(ShowCreationThenFadeAround(
|
||||
self.pendulum.theta_label
|
||||
))
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
self.pendulum.theta_label,
|
||||
theta_label,
|
||||
),
|
||||
FadeOut(q_mark)
|
||||
self.set_theta(
|
||||
60 * DEGREES,
|
||||
FadeIn(self.x_eq),
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
self.play(ShowCreation(opposite))
|
||||
self.play(ShowCreation(adjascent))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOut(opposite),
|
||||
FadeOut(adjascent),
|
||||
)
|
||||
self.play(
|
||||
Restore(vectors),
|
||||
FadeOut(g_line),
|
||||
FadeOut(arc),
|
||||
FadeOut(theta_label),
|
||||
)
|
||||
|
||||
vectors.resume_updating()
|
||||
|
||||
def ask_about_what_to_do(self):
|
||||
g_vect = self.g_vect
|
||||
|
|
@ -1215,103 +1508,22 @@ class AnalyzePendulumForce(MovingCameraScene):
|
|||
|
||||
self.play(FocusOn(pendulum.theta_label))
|
||||
self.play(Indicate(pendulum.theta_label))
|
||||
old_updaters = pendulum.get_updaters()
|
||||
pendulum.clear_updaters(recursive=False)
|
||||
pendulum.start_swinging()
|
||||
|
||||
pendulum_copy = pendulum.deepcopy()
|
||||
pendulum_copy.clear_updaters()
|
||||
pendulum_copy.fade(1)
|
||||
pendulum_copy.start_swinging()
|
||||
|
||||
def new_updater(p):
|
||||
p.set_theta(pendulum_copy.get_theta())
|
||||
pendulum.add_updater(new_updater)
|
||||
|
||||
self.add(pendulum_copy)
|
||||
self.wait(5)
|
||||
pendulum.clear_updaters()
|
||||
for updater in old_updaters:
|
||||
pendulum.add_updater(updater)
|
||||
|
||||
def show_arc_length(self):
|
||||
pendulum = self.pendulum
|
||||
to_fade = VGroup(
|
||||
self.g_vect,
|
||||
self.g_term,
|
||||
self.g_vect.component_lines,
|
||||
self.g_vect.tangent,
|
||||
self.g_vect.perp,
|
||||
self.g_sin_label,
|
||||
self.g_cos_label,
|
||||
)
|
||||
angle = pendulum.get_theta()
|
||||
height = pendulum.length
|
||||
top = pendulum.get_fixed_point()
|
||||
|
||||
line = Line(UP, DOWN)
|
||||
line.set_height(height)
|
||||
line.move_to(top, UP)
|
||||
arc = Arc(
|
||||
start_angle=-90 * DEGREES,
|
||||
angle=angle,
|
||||
arc_center=top,
|
||||
radius=height,
|
||||
stroke_color=GREEN,
|
||||
)
|
||||
|
||||
brace = Brace(Line(ORIGIN, 5 * UP), RIGHT)
|
||||
brace.point = VectorizedPoint(brace.get_right())
|
||||
brace.add(brace.point)
|
||||
brace.set_height(angle)
|
||||
brace.move_to(ORIGIN, DL)
|
||||
brace.apply_complex_function(np.exp)
|
||||
brace.scale(height)
|
||||
brace.rotate(-90 * DEGREES)
|
||||
brace.move_to(arc)
|
||||
brace.shift(MED_SMALL_BUFF * normalize(
|
||||
arc.point_from_proportion(0.5) - top
|
||||
))
|
||||
x_sym = TexMobject("x")
|
||||
x_sym.set_color(GREEN)
|
||||
x_sym.next_to(brace.point, DR, buff=SMALL_BUFF)
|
||||
|
||||
rhs = TexMobject("=", "L", "\\theta")
|
||||
rhs.set_color_by_tex("\\theta", BLUE)
|
||||
rhs.next_to(x_sym, RIGHT)
|
||||
rhs.shift(0.7 * SMALL_BUFF * UP)
|
||||
line_L = TexMobject("L")
|
||||
line_L.next_to(
|
||||
pendulum.rod.get_center(), UR, SMALL_BUFF,
|
||||
)
|
||||
|
||||
if hasattr(self, "traced_arcs"):
|
||||
self.play(FadeOut(self.traced_arcs))
|
||||
self.play(FadeOut(to_fade))
|
||||
self.play(
|
||||
ShowCreation(arc),
|
||||
Rotate(line, angle, about_point=top),
|
||||
UpdateFromAlphaFunc(
|
||||
line, lambda m, a: m.set_stroke(
|
||||
width=2 * there_and_back(a)
|
||||
)
|
||||
),
|
||||
GrowFromPoint(
|
||||
brace, line.get_bottom(),
|
||||
path_arc=angle
|
||||
),
|
||||
)
|
||||
self.play(FadeInFrom(x_sym, UP))
|
||||
self.wait()
|
||||
|
||||
# Show equation
|
||||
line.set_stroke(BLUE, 5)
|
||||
self.play(
|
||||
ShowCreationThenFadeOut(line),
|
||||
FadeInFromDown(line_L)
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
line_L, rhs.get_part_by_tex("L")
|
||||
),
|
||||
Write(rhs.get_part_by_tex("="))
|
||||
)
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
pendulum.theta_label,
|
||||
rhs.get_parts_by_tex("\\theta"),
|
||||
)
|
||||
)
|
||||
self.add(rhs)
|
||||
pendulum_copy.end_swinging()
|
||||
self.remove(pendulum_copy)
|
||||
pendulum.remove_updater(new_updater)
|
||||
self.update_mobjects(0)
|
||||
|
||||
def show_angular_velocity(self):
|
||||
pass
|
||||
|
|
@ -1664,6 +1876,18 @@ class BuildUpEquation(Scene):
|
|||
self.wait()
|
||||
|
||||
|
||||
class SimpleDampenedPendulum(Scene):
|
||||
def construct(self):
|
||||
pendulum = Pendulum(
|
||||
top_point=ORIGIN,
|
||||
initial_theta=150 * DEGREES,
|
||||
mu=0.5,
|
||||
)
|
||||
self.add(pendulum)
|
||||
pendulum.start_swinging()
|
||||
self.wait(20)
|
||||
|
||||
|
||||
class NewSceneName(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -108,10 +108,37 @@ class FormulasAreLies(PiCreatureScene):
|
|||
# pass
|
||||
|
||||
|
||||
class SoWhatIsThetaThen(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
ode = get_ode()
|
||||
ode.to_corner(UL)
|
||||
self.add(ode)
|
||||
|
||||
self.student_says(
|
||||
"Okay, but then\\\\"
|
||||
"what \\emph{is} $\\theta(t)$?"
|
||||
)
|
||||
self.wait()
|
||||
self.play(self.teacher.change, "happy")
|
||||
self.wait(2)
|
||||
self.teacher_says(
|
||||
"First, you must appreciate\\\\"
|
||||
"a deep truth...",
|
||||
added_anims=[self.get_student_changes(
|
||||
*3 * ["confused"]
|
||||
)]
|
||||
)
|
||||
self.wait(4)
|
||||
|
||||
|
||||
class ProveTeacherWrong(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
tex_config = {
|
||||
"tex_to_color_map": {"{\\theta}": BLUE}
|
||||
"tex_to_color_map": {
|
||||
"{\\theta}": BLUE,
|
||||
"{\\dot\\theta}": YELLOW,
|
||||
"{\\ddot\\theta}": RED,
|
||||
}
|
||||
}
|
||||
func = TexMobject(
|
||||
"{\\theta}(t)", "=",
|
||||
|
|
@ -119,23 +146,24 @@ class ProveTeacherWrong(TeacherStudentsScene):
|
|||
**tex_config,
|
||||
)
|
||||
d_func = TexMobject(
|
||||
"\\dot {\\theta}(t)", "=",
|
||||
"{\\dot\\theta}(t)", "=",
|
||||
"-\\left(\\sqrt{g / L}\\right)",
|
||||
"\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)",
|
||||
**tex_config,
|
||||
)
|
||||
dd_func = TexMobject(
|
||||
"\\ddot {\\theta}(t)", "=",
|
||||
"{\\ddot\\theta}(t)", "=",
|
||||
"-\\left(g / L\\right)",
|
||||
"\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)",
|
||||
**tex_config,
|
||||
)
|
||||
ode = TexMobject(
|
||||
"\\ddot {\\theta}({t})", "=",
|
||||
"-\\mu \\dot {\\theta}({t})",
|
||||
"-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||
**tex_config,
|
||||
)
|
||||
# ode = TexMobject(
|
||||
# "\\ddot {\\theta}({t})", "=",
|
||||
# "-\\mu \\dot {\\theta}({t})",
|
||||
# "-{g \\over L} \\sin\\big({\\theta}({t})\\big)",
|
||||
# **tex_config,
|
||||
# )
|
||||
ode = get_ode()
|
||||
arrows = [TexMobject("\\Downarrow") for x in range(2)]
|
||||
|
||||
VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7)
|
||||
|
|
@ -202,6 +230,58 @@ class ProveTeacherWrong(TeacherStudentsScene):
|
|||
self.wait(8)
|
||||
|
||||
|
||||
class PhysicistPhaseSpace(PiCreatureScene):
|
||||
def construct(self):
|
||||
physy = self.pi_creature
|
||||
name = TextMobject("Physicist")
|
||||
name.scale(1.5)
|
||||
name.to_corner(DL, buff=MED_SMALL_BUFF)
|
||||
physy.next_to(name, UP, SMALL_BUFF)
|
||||
VGroup(name, physy).shift_onto_screen()
|
||||
|
||||
axes = Axes(
|
||||
x_min=-1,
|
||||
x_max=10,
|
||||
y_min=-1,
|
||||
y_max=7,
|
||||
)
|
||||
axes.set_height(6)
|
||||
axes.next_to(physy, RIGHT)
|
||||
axes.to_edge(UP)
|
||||
axes.set_stroke(width=1)
|
||||
x_label = TextMobject("Position")
|
||||
x_label.next_to(axes.x_axis.get_right(), UP)
|
||||
y_label = TextMobject("Momentum")
|
||||
y_label.next_to(axes.y_axis.get_top(), RIGHT)
|
||||
|
||||
title = TextMobject("Phase space")
|
||||
title.scale(1.5)
|
||||
title.set_color(YELLOW)
|
||||
title.move_to(axes)
|
||||
|
||||
self.add(name, physy)
|
||||
|
||||
self.play(
|
||||
physy.change, "angry",
|
||||
Write(axes),
|
||||
FadeInFromDown(title)
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
GrowFromPoint(x_label, physy.get_corner(UR)),
|
||||
physy.change, "raise_right_hand",
|
||||
axes.x_axis.get_right()
|
||||
)
|
||||
self.play(
|
||||
GrowFromPoint(y_label, physy.get_corner(UR)),
|
||||
physy.look_at, axes.y_axis.get_top(),
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
def create_pi_creature(self):
|
||||
return PiCreature(color=GREY).to_corner(DL)
|
||||
|
||||
|
||||
class AskAboutActuallySolving(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
ode = get_ode()
|
||||
|
|
@ -211,7 +291,7 @@ class AskAboutActuallySolving(TeacherStudentsScene):
|
|||
|
||||
self.student_says(
|
||||
"Yeah yeah, but how do\\\\"
|
||||
"you acutally \\emph{solve} it?",
|
||||
"you actually \\emph{solve} it?",
|
||||
student_index=1,
|
||||
target_mode="sassy",
|
||||
added_anims=[morty.change, "thinking"],
|
||||
|
|
@ -242,9 +322,7 @@ class HungerForExactness(TeacherStudentsScene):
|
|||
ode.to_corner(UL)
|
||||
left_part = ode[:5]
|
||||
friction_part = ode[5:11]
|
||||
right_part = ode[11:]
|
||||
self.add(ode)
|
||||
frictionless_group = VGroup(left_part, right_part)
|
||||
|
||||
proposed_solution = TexMobject(
|
||||
"\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}"
|
||||
|
|
@ -376,9 +454,62 @@ class HungerForExactness(TeacherStudentsScene):
|
|||
)
|
||||
self.wait()
|
||||
self.wait(3)
|
||||
self.change_student_modes("tired", "sad", "concerned_musician")
|
||||
self.wait(4)
|
||||
self.look_at(solution)
|
||||
self.wait(5)
|
||||
self.play(
|
||||
FadeOutAndShift(solution, 2 * LEFT),
|
||||
Restore(ode),
|
||||
self.get_student_changes(*3 * ["sick"])
|
||||
self.get_student_changes(
|
||||
"sick", "angry", "tired",
|
||||
)
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
mystery = TexMobject(
|
||||
"\\theta(t) = ???",
|
||||
tex_to_color_map={"\\theta": BLUE},
|
||||
)
|
||||
mystery.scale(2)
|
||||
mystery.to_edge(UP)
|
||||
mystery.set_stroke(width=0, background=True)
|
||||
mystery_boundary = AnimatedBoundary(
|
||||
mystery, stroke_width=1
|
||||
)
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(mystery),
|
||||
self.teacher.change, "pondering"
|
||||
)
|
||||
self.add(mystery_boundary, mystery)
|
||||
self.change_all_student_modes("sad")
|
||||
self.look_at(mystery)
|
||||
self.wait(5)
|
||||
|
||||
# Define
|
||||
self.student_says(
|
||||
"Let $\\text{P}(\\mu, g, L; t)$ be a\\\\"
|
||||
"function satisfying this ODE.",
|
||||
student_index=0,
|
||||
target_mode="speaking",
|
||||
added_anims=[
|
||||
FadeOut(mystery),
|
||||
FadeOut(mystery_boundary),
|
||||
ode.to_corner, UR
|
||||
]
|
||||
)
|
||||
self.change_student_modes(
|
||||
"hooray", "sassy", "sassy",
|
||||
look_at_arg=students[0].eyes.get_corner(UR),
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
|
||||
class ItGetsWorse(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says("It gets\\\\worse")
|
||||
self.change_student_modes(
|
||||
"hesitant", "pleading", "erm"
|
||||
)
|
||||
self.wait(5)
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ def get_ode():
|
|||
tex_config = {
|
||||
"tex_to_color_map": {
|
||||
"{\\theta}": BLUE,
|
||||
"{\\dot\\theta}": YELLOW,
|
||||
"{\\ddot\\theta}": RED,
|
||||
"{\\dot\\theta}": RED,
|
||||
"{\\ddot\\theta}": YELLOW,
|
||||
"{t}": WHITE,
|
||||
"{\\mu}": WHITE,
|
||||
}
|
||||
|
|
@ -35,6 +35,16 @@ def get_ode():
|
|||
return ode
|
||||
|
||||
|
||||
def get_period_formula():
|
||||
return TexMobject(
|
||||
"2\\pi", "\\sqrt{\\,", "L", "/", "g", "}",
|
||||
tex_to_color_map={
|
||||
"L": BLUE,
|
||||
"g": YELLOW,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def pendulum_vector_field_func(point, mu=0.1, g=9.8, L=3):
|
||||
theta, omega = point[:2]
|
||||
return np.array([
|
||||
|
|
@ -54,3 +64,55 @@ def get_vector_symbol(*texs, **kwargs):
|
|||
config.update(kwargs)
|
||||
array = [[tex] for tex in texs]
|
||||
return Matrix(array, **config)
|
||||
|
||||
|
||||
def get_heart_var(index):
|
||||
heart = SuitSymbol("hearts")
|
||||
if index == 1:
|
||||
heart.set_color(BLUE_C)
|
||||
elif index == 2:
|
||||
heart.set_color(GREEN)
|
||||
heart.set_height(0.7)
|
||||
index = Integer(index)
|
||||
index.move_to(heart.get_corner(DR))
|
||||
heart.add(index)
|
||||
return heart
|
||||
|
||||
|
||||
def get_heart_var_deriv(index):
|
||||
heart = get_heart_var(index)
|
||||
filler_tex = "T"
|
||||
deriv = TexMobject("{d", filler_tex, "\\over", "dt}")
|
||||
deriv.scale(2)
|
||||
filler = deriv.get_part_by_tex(filler_tex)
|
||||
heart.match_height(filler)
|
||||
heart.move_to(filler)
|
||||
heart.scale(1.5, about_edge=UL)
|
||||
deriv.remove(filler)
|
||||
deriv.add(heart)
|
||||
deriv.heart = heart
|
||||
return deriv
|
||||
|
||||
|
||||
def get_love_equation1():
|
||||
equation = VGroup(
|
||||
get_heart_var_deriv(1),
|
||||
TexMobject("=").scale(2),
|
||||
TexMobject("a").scale(2),
|
||||
get_heart_var(2)
|
||||
)
|
||||
equation.arrange(RIGHT)
|
||||
equation[-1].shift(SMALL_BUFF * DL)
|
||||
return equation
|
||||
|
||||
|
||||
def get_love_equation2():
|
||||
equation = VGroup(
|
||||
get_heart_var_deriv(2),
|
||||
TexMobject("=").scale(2),
|
||||
TexMobject("-b").scale(2),
|
||||
get_heart_var(1),
|
||||
)
|
||||
equation.arrange(RIGHT)
|
||||
equation[-1].shift(SMALL_BUFF * DL)
|
||||
return equation
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,6 +37,40 @@ class SmallAngleApproximationTex(Scene):
|
|||
|
||||
class StrogatzQuote(Scene):
|
||||
def construct(self):
|
||||
quote = self.get_quote()
|
||||
movers = VGroup(*quote[:-1].family_members_with_points())
|
||||
for mover in movers:
|
||||
mover.save_state()
|
||||
disc = Circle(radius=0.05)
|
||||
disc.set_stroke(width=0)
|
||||
disc.set_fill(BLACK, 0)
|
||||
disc.move_to(mover)
|
||||
mover.become(disc)
|
||||
self.play(
|
||||
FadeInFrom(quote.author_part, LEFT),
|
||||
LaggedStartMap(
|
||||
# FadeInFromLarge,
|
||||
# quote[:-1].family_members_with_points(),
|
||||
Restore, movers,
|
||||
lag_ratio=0.005,
|
||||
run_time=2,
|
||||
)
|
||||
# FadeInFromDown(quote[:-1]),
|
||||
# lag_ratio=0.01,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(quote.law_part.copy().set_color(YELLOW)),
|
||||
run_time=1,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(quote.language_part.copy().set_color(BLUE)),
|
||||
run_time=1.5,
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
def get_quote(self):
|
||||
law_words = "laws of physics"
|
||||
language_words = "language of differential equations"
|
||||
author = "-Steven Strogatz"
|
||||
|
|
@ -51,47 +85,342 @@ class StrogatzQuote(Scene):
|
|||
arg_separator=" ",
|
||||
substrings_to_isolate=[law_words, language_words, author]
|
||||
)
|
||||
law_part = quote.get_part_by_tex(law_words)
|
||||
language_part = quote.get_part_by_tex(language_words)
|
||||
author_part = quote.get_part_by_tex(author)
|
||||
quote.law_part = quote.get_part_by_tex(law_words)
|
||||
quote.language_part = quote.get_part_by_tex(language_words)
|
||||
quote.author_part = quote.get_part_by_tex(author)
|
||||
quote.set_width(12)
|
||||
quote.to_edge(UP)
|
||||
quote[-2].shift(SMALL_BUFF * LEFT)
|
||||
author_part.shift(RIGHT + 0.5 * DOWN)
|
||||
author_part.scale(1.2, about_edge=UL)
|
||||
quote.author_part.shift(RIGHT + 0.5 * DOWN)
|
||||
quote.author_part.scale(1.2, about_edge=UL)
|
||||
|
||||
movers = VGroup(*quote[:-1].family_members_with_points())
|
||||
for mover in movers:
|
||||
mover.save_state()
|
||||
disc = Circle(radius=0.05)
|
||||
disc.set_stroke(width=0)
|
||||
disc.set_fill(BLACK, 0)
|
||||
disc.move_to(mover)
|
||||
mover.become(disc)
|
||||
return quote
|
||||
|
||||
|
||||
class WriteInRadians(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("In radians")
|
||||
words.set_color(YELLOW)
|
||||
square = SurroundingRectangle(TexMobject("\\theta"))
|
||||
square.next_to(words, UP)
|
||||
self.play(ShowCreation(square))
|
||||
self.play(Write(words), FadeOut(square))
|
||||
self.wait()
|
||||
|
||||
|
||||
class XEqLThetaToCorner(Scene):
|
||||
def construct(self):
|
||||
equation = TexMobject(
|
||||
"x = L\\theta",
|
||||
tex_to_color_map={
|
||||
"x": GREEN,
|
||||
"\\theta": BLUE,
|
||||
}
|
||||
)
|
||||
equation.move_to(DOWN + 3 * RIGHT)
|
||||
self.add(equation)
|
||||
self.play(equation.to_corner, DL, {"buff": LARGE_BUFF})
|
||||
self.wait()
|
||||
|
||||
|
||||
class ComingUp(Scene):
|
||||
CONFIG = {
|
||||
"camera_config": {"background_color": DARKER_GREY}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
frame = ScreenRectangle(
|
||||
stroke_width=0,
|
||||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
height=6
|
||||
)
|
||||
title = TextMobject("Coming up")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
frame.next_to(title, DOWN)
|
||||
animated_frame = AnimatedBoundary(frame)
|
||||
self.add(frame, title, animated_frame)
|
||||
self.wait(10)
|
||||
|
||||
|
||||
class InputLabel(Scene):
|
||||
def construct(self):
|
||||
label = TextMobject("Input")
|
||||
label.scale(1.25)
|
||||
arrow = Vector(UP)
|
||||
arrow.next_to(label, UP)
|
||||
self.play(
|
||||
FadeInFrom(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,
|
||||
FadeInFrom(label, UP),
|
||||
GrowArrow(arrow)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ReallyHardToSolve(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject(
|
||||
"They're", "really\\\\",
|
||||
"freaking", "hard\\\\",
|
||||
"to", "solve\\\\",
|
||||
)
|
||||
words.set_height(6)
|
||||
|
||||
self.wait()
|
||||
for word in words:
|
||||
wait_time = 0.05 * len(word)
|
||||
self.add(word)
|
||||
self.wait(wait_time)
|
||||
self.wait()
|
||||
|
||||
|
||||
class ReasonForSolution(Scene):
|
||||
def construct(self):
|
||||
# Words
|
||||
eq_word = TextMobject("Differential\\\\Equation")
|
||||
s_word = TextMobject("Solution")
|
||||
u_word = TextMobject("Understanding")
|
||||
c_word = TextMobject("Computation")
|
||||
cu_group = VGroup(u_word, c_word)
|
||||
cu_group.arrange(DOWN, buff=2)
|
||||
group = VGroup(eq_word, s_word, cu_group)
|
||||
group.arrange(RIGHT, buff=2)
|
||||
words = VGroup(eq_word, s_word, u_word, c_word)
|
||||
|
||||
# Arrows
|
||||
arrows = VGroup(
|
||||
Arrow(eq_word.get_right(), s_word.get_left()),
|
||||
Arrow(s_word.get_right(), u_word.get_left()),
|
||||
Arrow(s_word.get_right(), c_word.get_left()),
|
||||
)
|
||||
arrows.set_color(LIGHT_GREY)
|
||||
new_arrows = VGroup(
|
||||
Arrow(
|
||||
eq_word.get_corner(UR),
|
||||
u_word.get_left(),
|
||||
path_arc=-60 * DEGREES,
|
||||
),
|
||||
Arrow(
|
||||
eq_word.get_corner(DR),
|
||||
c_word.get_left(),
|
||||
path_arc=60 * DEGREES,
|
||||
),
|
||||
)
|
||||
new_arrows.set_color(BLUE)
|
||||
|
||||
# Define first examples
|
||||
t2c = {
|
||||
"{x}": BLUE,
|
||||
"{\\dot x}": RED,
|
||||
}
|
||||
equation = TexMobject(
|
||||
"{\\dot x}(t) = k {x}(t)",
|
||||
tex_to_color_map=t2c,
|
||||
)
|
||||
equation.next_to(eq_word, DOWN)
|
||||
solution = TexMobject(
|
||||
"{x}(t) = x_0 e^{kt}",
|
||||
tex_to_color_map=t2c,
|
||||
)
|
||||
solution.next_to(s_word, DOWN, MED_LARGE_BUFF)
|
||||
equation.align_to(solution, DOWN)
|
||||
|
||||
axes = Axes(
|
||||
x_min=-1,
|
||||
x_max=5.5,
|
||||
y_min=-1,
|
||||
y_max=4.5,
|
||||
y_axis_config={"unit_size": 0.5}
|
||||
)
|
||||
axes.set_stroke(width=2)
|
||||
graph_line = axes.get_graph(
|
||||
lambda x: np.exp(0.4 * x)
|
||||
)
|
||||
graph_line.set_stroke(width=2)
|
||||
graph = VGroup(axes, graph_line)
|
||||
graph.scale(0.5)
|
||||
graph.next_to(u_word, UP)
|
||||
|
||||
computation = TexMobject(
|
||||
# "\\displaystyle "
|
||||
"e^x = \\sum_{n=0}^\\infty "
|
||||
"\\frac{x^n}{n!}"
|
||||
)
|
||||
computation.next_to(c_word, DOWN)
|
||||
|
||||
first_examples = VGroup(
|
||||
equation, solution, graph, computation
|
||||
)
|
||||
|
||||
# Second example
|
||||
ode = get_ode()
|
||||
ode.scale(0.75)
|
||||
second_examples = VGroup(
|
||||
ode,
|
||||
TexMobject("???").set_color(LIGHT_GREY),
|
||||
ScreenRectangle(
|
||||
height=2,
|
||||
stroke_width=1,
|
||||
),
|
||||
)
|
||||
for fe, se in zip(first_examples, second_examples):
|
||||
se.move_to(fe, DOWN)
|
||||
|
||||
ode.shift(2 * SMALL_BUFF * DOWN)
|
||||
ode.add_to_back(BackgroundRectangle(ode[-4:]))
|
||||
|
||||
self.add(eq_word)
|
||||
self.add(equation)
|
||||
self.play(
|
||||
FadeInFrom(s_word, LEFT),
|
||||
GrowArrow(arrows[0]),
|
||||
TransformFromCopy(equation, solution)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(law_part.copy().set_color(YELLOW)),
|
||||
run_time=1,
|
||||
FadeInFrom(c_word, UL),
|
||||
GrowArrow(arrows[2]),
|
||||
FadeInFrom(computation, UP)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
Write(language_part.copy().set_color(BLUE)),
|
||||
run_time=1.5,
|
||||
FadeInFrom(u_word, DL),
|
||||
GrowArrow(arrows[1]),
|
||||
FadeInFromDown(graph)
|
||||
)
|
||||
self.wait(2)
|
||||
|
||||
self.play(
|
||||
FadeOut(first_examples),
|
||||
FadeIn(second_examples[:2])
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
arrows.fade, 0.75,
|
||||
s_word.fade, 0.75,
|
||||
second_examples[1].fade, 0.75,
|
||||
ShowCreation(new_arrows[0]),
|
||||
FadeIn(second_examples[2])
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(new_arrows[1]),
|
||||
Animation(second_examples),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class WritePhaseSpace(Scene):
|
||||
def construct(self):
|
||||
word = TextMobject("Phase space")
|
||||
word.scale(2)
|
||||
word.shift(FRAME_WIDTH * LEFT / 4)
|
||||
word.to_edge(UP)
|
||||
word.add_background_rectangle()
|
||||
|
||||
lines = VGroup(*[
|
||||
Line(v, 1.3 * v)
|
||||
for v in compass_directions(50)
|
||||
])
|
||||
lines.replace(word, stretch=True)
|
||||
lines.scale(1.5)
|
||||
lines.set_stroke(YELLOW)
|
||||
lines.shuffle()
|
||||
|
||||
self.add(word)
|
||||
self.play(
|
||||
ShowPassingFlashWithThinningStrokeWidth(
|
||||
lines,
|
||||
lag_ratio=0.002,
|
||||
run_time=1.5,
|
||||
time_width=0.9,
|
||||
n_segments=5,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class GleickQuote(Scene):
|
||||
def construct(self):
|
||||
quote = TextMobject(
|
||||
"``[Phase space is] one of the most\\\\",
|
||||
"powerful inventions", "of modern science.''\\\\",
|
||||
)
|
||||
quote.power_part = quote.get_part_by_tex("power")
|
||||
book = ImageMobject("ChaosBookCover")
|
||||
book.set_height(5)
|
||||
book.next_to(ORIGIN, LEFT)
|
||||
book.to_edge(DOWN)
|
||||
gleick = ImageMobject("JamesGleick")
|
||||
gleick.set_height(5)
|
||||
gleick.next_to(ORIGIN, RIGHT)
|
||||
gleick.to_edge(DOWN)
|
||||
quote.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
FadeInFrom(book, RIGHT),
|
||||
FadeInFrom(gleick, LEFT),
|
||||
)
|
||||
self.wait()
|
||||
self.play(Write(quote))
|
||||
self.play(Write(
|
||||
quote.power_part.copy().set_color(BLUE),
|
||||
run_time=1
|
||||
))
|
||||
self.wait()
|
||||
|
||||
|
||||
class WritePhaseFlow(Scene):
|
||||
def construct(self):
|
||||
words = TextMobject("Phase flow")
|
||||
words.scale(2)
|
||||
words.shift(FRAME_WIDTH * LEFT / 4)
|
||||
words.to_edge(UP)
|
||||
words.add_background_rectangle()
|
||||
self.play(Write(words))
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowSineValues(Scene):
|
||||
def construct(self):
|
||||
angle_tracker = ValueTracker(60 * DEGREES)
|
||||
get_angle = angle_tracker.get_value
|
||||
formula = always_redraw(
|
||||
lambda: self.get_sine_formula(get_angle())
|
||||
)
|
||||
self.add(formula)
|
||||
|
||||
self.play(
|
||||
angle_tracker.set_value, 0,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
angle_tracker.set_value, 90 * DEGREES,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
def get_sine_formula(self, angle):
|
||||
sin, lp, rp = TexMobject(
|
||||
"\\sin", "(", ") = "
|
||||
)
|
||||
input_part = Integer(
|
||||
angle / DEGREES,
|
||||
unit="^\\circ",
|
||||
)
|
||||
input_part.set_color(YELLOW)
|
||||
output_part = DecimalNumber(
|
||||
np.sin(input_part.get_value() * DEGREES),
|
||||
num_decimal_places=3,
|
||||
)
|
||||
result = VGroup(
|
||||
sin, lp, input_part, rp, output_part
|
||||
)
|
||||
result.arrange(RIGHT, buff=SMALL_BUFF)
|
||||
sin.scale(1.1, about_edge=DOWN)
|
||||
lp.align_to(rp, UP)
|
||||
return result
|
||||
|
||||
|
||||
class SetAsideSeekingSolution(Scene):
|
||||
def construct(self):
|
||||
|
|
@ -141,6 +470,14 @@ class SetAsideSeekingSolution(Scene):
|
|||
)
|
||||
|
||||
|
||||
class ThreeBodyTitle(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("Three body problem")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
self.add(title)
|
||||
|
||||
|
||||
class ThreeBodySymbols(Scene):
|
||||
def construct(self):
|
||||
self.init_coord_groups()
|
||||
|
|
@ -260,3 +597,259 @@ class ThreeBodySymbols(Scene):
|
|||
rate_func=linear,
|
||||
)
|
||||
self.play(FadeOut(coord_copies))
|
||||
|
||||
|
||||
class ThreeBodyEquation(Scene):
|
||||
def construct(self):
|
||||
x1 = "\\vec{\\textbf{x}}_1"
|
||||
x2 = "\\vec{\\textbf{x}}_2"
|
||||
x3 = "\\vec{\\textbf{x}}_3"
|
||||
kw = {
|
||||
"tex_to_color_map": {
|
||||
x1: RED,
|
||||
x2: GREEN,
|
||||
x3: BLUE,
|
||||
}
|
||||
}
|
||||
equations = VGroup(*[
|
||||
TexMobject(
|
||||
"{d^2", t1, "\\over dt^2}", "=",
|
||||
"G", "\\left("
|
||||
"{" + m2, "(", t2, "-", t1, ")"
|
||||
"\\over"
|
||||
"||", t2, "-", t1, "||^3}",
|
||||
"+",
|
||||
"{" + m3, "(", t3, "-", t1, ")"
|
||||
"\\over"
|
||||
"||", t3, "-", t1, "||^3}",
|
||||
"\\right)",
|
||||
**kw
|
||||
)
|
||||
for t1, t2, t3, m1, m2, m3 in [
|
||||
(x1, x2, x3, "m_1", "m_2", "m_3"),
|
||||
(x2, x3, x1, "m_2", "m_3", "m_1"),
|
||||
(x3, x1, x2, "m_3", "m_1", "m_2"),
|
||||
]
|
||||
])
|
||||
equations.arrange(DOWN, buff=LARGE_BUFF)
|
||||
|
||||
self.play(LaggedStartMap(
|
||||
FadeInFrom, equations,
|
||||
lambda m: (m, UP),
|
||||
lag_ratio=0.2,
|
||||
))
|
||||
self.wait()
|
||||
|
||||
|
||||
class JumpToThisPoint(Scene):
|
||||
def construct(self):
|
||||
dot = Dot(color=YELLOW)
|
||||
dot.scale(0.5)
|
||||
|
||||
arrow = Vector(DR, color=WHITE)
|
||||
arrow.next_to(dot, UL, SMALL_BUFF)
|
||||
words = TextMobject(
|
||||
"Jump directly to\\\\",
|
||||
"this point?",
|
||||
)
|
||||
words.add_background_rectangle_to_submobjects()
|
||||
words.next_to(arrow.get_start(), UP, SMALL_BUFF)
|
||||
|
||||
self.play(
|
||||
FadeInFromLarge(dot, 20),
|
||||
rate_func=rush_into,
|
||||
)
|
||||
self.play(
|
||||
GrowArrow(arrow),
|
||||
FadeInFromDown(words),
|
||||
)
|
||||
|
||||
|
||||
class ChaosTitle(Scene):
|
||||
def construct(self):
|
||||
title = TextMobject("Chaos theory")
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
line = Line(LEFT, RIGHT)
|
||||
line.set_width(FRAME_WIDTH - 1)
|
||||
line.next_to(title, DOWN)
|
||||
|
||||
self.play(
|
||||
Write(title),
|
||||
ShowCreation(line),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class RevisitQuote(StrogatzQuote, PiCreatureScene):
|
||||
def construct(self):
|
||||
quote = self.get_quote()
|
||||
quote.law_part.set_color(YELLOW)
|
||||
quote.language_part.set_color(BLUE)
|
||||
quote.set_stroke(BLACK, 6, background=True)
|
||||
quote.scale(0.8, about_edge=UL)
|
||||
|
||||
new_langauge_part = TextMobject(
|
||||
"\\Large Language of differential equations"
|
||||
)
|
||||
new_langauge_part.to_edge(UP)
|
||||
new_langauge_part.match_style(quote.language_part)
|
||||
|
||||
randy = self.pi_creature
|
||||
|
||||
self.play(
|
||||
FadeInFrom(quote[:-1], DOWN),
|
||||
FadeInFrom(quote[-1:], LEFT),
|
||||
randy.change, "raise_right_hand",
|
||||
)
|
||||
self.play(Blink(randy))
|
||||
self.play(randy.change, "angry")
|
||||
self.play(
|
||||
Blink(randy),
|
||||
VFadeOut(randy, run_time=3)
|
||||
)
|
||||
mover = VGroup(quote.language_part)
|
||||
self.add(quote, mover)
|
||||
self.play(
|
||||
ReplacementTransform(
|
||||
mover, new_langauge_part,
|
||||
),
|
||||
*[
|
||||
FadeOut(part)
|
||||
for part in quote
|
||||
if part is not quote.language_part
|
||||
],
|
||||
run_time=2,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class EndScreen(PatreonEndScreen):
|
||||
CONFIG = {
|
||||
"specific_patrons": [
|
||||
"Juan Benet",
|
||||
"Vassili Philippov",
|
||||
"Burt Humburg",
|
||||
"Carlos Vergara Del Rio",
|
||||
"Matt Russell",
|
||||
"Scott Gray",
|
||||
"soekul",
|
||||
"Tihan Seale",
|
||||
"Ali Yahya",
|
||||
"dave nicponski",
|
||||
"Evan Phillips",
|
||||
"Graham",
|
||||
"Joseph Kelly",
|
||||
"Kaustuv DeBiswas",
|
||||
"LambdaLabs",
|
||||
"Lukas Biewald",
|
||||
"Mike Coleman",
|
||||
"Peter Mcinerney",
|
||||
"Quantopian",
|
||||
"Roy Larson",
|
||||
"Scott Walter, Ph.D.",
|
||||
"Yana Chernobilsky",
|
||||
"Yu Jun",
|
||||
"Jordan Scales",
|
||||
"Lukas -krtek.net- Novy",
|
||||
"John Shaughnessy",
|
||||
"Britt Selvitelle",
|
||||
"David Gow",
|
||||
"J",
|
||||
"Jonathan Wilson",
|
||||
"Joseph John Cox",
|
||||
"Magnus Dahlström",
|
||||
"Randy C. Will",
|
||||
"Ryan Atallah",
|
||||
"Luc Ritchie",
|
||||
"1stViewMaths",
|
||||
"Adrian Robinson",
|
||||
"Alexis Olson",
|
||||
"Andreas Benjamin Brössel",
|
||||
"Andrew Busey",
|
||||
"Ankalagon",
|
||||
"Antonio Juarez",
|
||||
"Arjun Chakroborty",
|
||||
"Art Ianuzzi",
|
||||
"Awoo",
|
||||
"Bernd Sing",
|
||||
"Boris Veselinovich",
|
||||
"Brian Staroselsky",
|
||||
"Chad Hurst",
|
||||
"Charles Southerland",
|
||||
"Chris Connett",
|
||||
"Christian Kaiser",
|
||||
"Clark Gaebel",
|
||||
"Cooper Jones",
|
||||
"Danger Dai",
|
||||
"Dave B",
|
||||
"Dave Kester",
|
||||
"David Clark",
|
||||
"DeathByShrimp",
|
||||
"Delton Ding",
|
||||
"eaglle",
|
||||
"emptymachine",
|
||||
"Eric Younge",
|
||||
"Eryq Ouithaqueue",
|
||||
"Federico Lebron",
|
||||
"Giovanni Filippi",
|
||||
"Hal Hildebrand",
|
||||
"Herman Dieset",
|
||||
"Hitoshi Yamauchi",
|
||||
"Isaac Jeffrey Lee",
|
||||
"j eduardo perez",
|
||||
"Jacob Magnuson",
|
||||
"Jameel Syed",
|
||||
"Jason Hise",
|
||||
"Jeff Linse",
|
||||
"Jeff Straathof",
|
||||
"John Griffith",
|
||||
"John Haley",
|
||||
"John V Wertheim",
|
||||
"Jonathan Eppele",
|
||||
"Kai-Siang Ang",
|
||||
"Kanan Gill",
|
||||
"L0j1k",
|
||||
"Lee Redden",
|
||||
"Linh Tran",
|
||||
"Ludwig Schubert",
|
||||
"Magister Mugit",
|
||||
"Mark B Bahu",
|
||||
"Mark Heising",
|
||||
"Martin Price",
|
||||
"Mathias Jansson",
|
||||
"Matt Langford",
|
||||
"Matt Roveto",
|
||||
"Matthew Cocke",
|
||||
"Michael Faust",
|
||||
"Michael Hardel",
|
||||
"Mirik Gogri",
|
||||
"Mustafa Mahdi",
|
||||
"Márton Vaitkus",
|
||||
"Nero Li",
|
||||
"Nikita Lesnikov",
|
||||
"Omar Zrien",
|
||||
"Owen Campbell-Moore",
|
||||
"Peter Ehrnstrom",
|
||||
"RedAgent14",
|
||||
"rehmi post",
|
||||
"Richard Burgmann",
|
||||
"Richard Comish",
|
||||
"Ripta Pasay",
|
||||
"Rish Kundalia",
|
||||
"Robert Teed",
|
||||
"Roobie",
|
||||
"Ryan Williams",
|
||||
"Samuel D. Judge",
|
||||
"Solara570",
|
||||
"Stevie Metke",
|
||||
"Tal Einav",
|
||||
"Ted Suzman",
|
||||
"Valeriy Skobelev",
|
||||
"Xavier Bernard",
|
||||
"Yavor Ivanov",
|
||||
"Yaw Etse",
|
||||
"YinYangBalance.Asia",
|
||||
"Zach Cardwell",
|
||||
]
|
||||
}
|
||||
|
|
|
|||
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))
|
||||
else:
|
||||
args_list.append((submob,))
|
||||
anim_kwargs = dict(kwargs)
|
||||
if "lag_ratio" in anim_kwargs:
|
||||
anim_kwargs.pop("lag_ratio")
|
||||
animations = [
|
||||
AnimationClass(*args, **kwargs)
|
||||
AnimationClass(*args, **anim_kwargs)
|
||||
for args in args_list
|
||||
]
|
||||
super().__init__(*animations, **kwargs)
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class PatreonThanks(Scene):
|
|||
patreon_logo = PatreonLogo()
|
||||
patreon_logo.to_edge(UP)
|
||||
|
||||
patrons = list(map(TextMobject, self.specific_patrons))
|
||||
patrons = list(map(TextMobject, self.specific_patronds))
|
||||
num_groups = float(len(patrons)) / self.max_patron_group_size
|
||||
proportion_range = np.linspace(0, 1, num_groups + 1)
|
||||
indices = (len(patrons) * proportion_range).astype('int')
|
||||
|
|
@ -213,7 +213,14 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||
underline.next_to(thanks, DOWN, SMALL_BUFF)
|
||||
thanks.add(underline)
|
||||
|
||||
patrons = VGroup(*list(map(TextMobject, self.specific_patrons)))
|
||||
changed_patron_names = map(
|
||||
self.modify_patron_name,
|
||||
self.specific_patrons,
|
||||
)
|
||||
patrons = VGroup(*map(
|
||||
TextMobject,
|
||||
changed_patron_names,
|
||||
))
|
||||
patrons.scale(self.patron_scale_val)
|
||||
for patron in patrons:
|
||||
if patron.get_width() > self.max_patron_width:
|
||||
|
|
@ -252,6 +259,11 @@ class PatreonEndScreen(PatreonThanks, PiCreatureScene):
|
|||
self.add(columns, black_rect, line, thanks)
|
||||
self.wait(wait_time)
|
||||
|
||||
def modify_patron_name(self, name):
|
||||
if name is "RedAgent14":
|
||||
return "Brian Shepetofsky"
|
||||
return name
|
||||
|
||||
|
||||
class LogoGenerationTemplate(MovingCameraScene):
|
||||
def setup(self):
|
||||
|
|
|
|||
|
|
@ -174,6 +174,7 @@ class PiCreatureScene(Scene):
|
|||
continue
|
||||
anims_with_pi_creature = [anim for anim in animations if pi_creature in anim.mobject.get_family()]
|
||||
for anim in anims_with_pi_creature:
|
||||
continue # TODO, this is broken
|
||||
if isinstance(anim, Transform):
|
||||
index = anim.mobject.get_family().index(pi_creature)
|
||||
target_family = anim.target_mobject.get_family()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ class AnimatedBoundary(VGroup):
|
|||
"colors": [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
|
||||
"max_stroke_width": 3,
|
||||
"cycle_rate": 0.5,
|
||||
"back_and_forth": True,
|
||||
"draw_rate_func": smooth,
|
||||
"fade_rate_func": smooth,
|
||||
}
|
||||
|
||||
def __init__(self, vmobject, **kwargs):
|
||||
|
|
@ -22,9 +25,11 @@ class AnimatedBoundary(VGroup):
|
|||
]
|
||||
self.add(*self.boundary_copies)
|
||||
self.total_time = 0
|
||||
self.add_updater(lambda m, dt: self.update(dt))
|
||||
self.add_updater(
|
||||
lambda m, dt: self.update_boundary_copies(dt)
|
||||
)
|
||||
|
||||
def update(self, dt):
|
||||
def update_boundary_copies(self, dt):
|
||||
# Not actual time, but something which passes at
|
||||
# an altered rate to make the implementation below
|
||||
# cleaner
|
||||
|
|
@ -35,12 +40,14 @@ class AnimatedBoundary(VGroup):
|
|||
vmobject = self.vmobject
|
||||
|
||||
index = int(time % len(colors))
|
||||
alpha = smooth(time % 1)
|
||||
alpha = time % 1
|
||||
draw_alpha = self.draw_rate_func(alpha)
|
||||
fade_alpha = self.fade_rate_func(alpha)
|
||||
|
||||
if int(time) % 2 == 0:
|
||||
bounds = (0, alpha)
|
||||
if self.back_and_forth and int(time) % 1 == 0:
|
||||
bounds = (1 - draw_alpha, 1)
|
||||
else:
|
||||
bounds = (1 - alpha, 1)
|
||||
bounds = (0, draw_alpha)
|
||||
self.full_family_become_partial(growing, vmobject, *bounds)
|
||||
growing.set_stroke(colors[index], width=msw)
|
||||
|
||||
|
|
@ -48,7 +55,7 @@ class AnimatedBoundary(VGroup):
|
|||
self.full_family_become_partial(fading, vmobject, 0, 1)
|
||||
fading.set_stroke(
|
||||
color=colors[index - 1],
|
||||
width=(1 - alpha) * msw
|
||||
width=(1 - fade_alpha) * msw
|
||||
)
|
||||
|
||||
self.total_time += dt
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class CoordinateSystem():
|
|||
)[0],
|
||||
target=x,
|
||||
lower_bound=self.x_min,
|
||||
uplper_bound=self.x_max,
|
||||
upper_bound=self.x_max,
|
||||
)
|
||||
if alpha is not None:
|
||||
return graph.point_from_proportion(alpha)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ def always(method, *args, **kwargs):
|
|||
|
||||
def f_always(method, *arg_generators, **kwargs):
|
||||
"""
|
||||
More functional version of always, where insetead
|
||||
More functional version of always, where instead
|
||||
of taking in args, it takes in functions which ouput
|
||||
the relevant arguments.
|
||||
"""
|
||||
|
|
@ -77,16 +77,15 @@ def turn_animation_into_updater(animation, cycle=False, **kwargs):
|
|||
|
||||
def update(m, dt):
|
||||
run_time = animation.get_run_time()
|
||||
alpha = np.clip(
|
||||
animation.total_time / run_time,
|
||||
0, 1,
|
||||
)
|
||||
time_ratio = animation.total_time / run_time
|
||||
if cycle:
|
||||
animation.total_time = animation.total_time % run_time
|
||||
elif alpha >= 1:
|
||||
animation.finish()
|
||||
m.remove_updater(update)
|
||||
return
|
||||
alpha = time_ratio % 1
|
||||
else:
|
||||
alpha = np.clip(time_ratio, 0, 1)
|
||||
if alpha >= 1:
|
||||
animation.finish()
|
||||
m.remove_updater(update)
|
||||
return
|
||||
animation.interpolate(alpha)
|
||||
animation.update_mobjects(dt)
|
||||
animation.total_time += dt
|
||||
|
|
|
|||
|
|
@ -95,9 +95,10 @@ class NumberLine(Line):
|
|||
)
|
||||
|
||||
def get_tick_numbers(self):
|
||||
u = -1 if self.include_tip else 1
|
||||
return np.arange(
|
||||
self.leftmost_tick,
|
||||
self.x_max + self.tick_frequency / 2,
|
||||
self.x_max + u * self.tick_frequency / 2,
|
||||
self.tick_frequency
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -393,11 +393,13 @@ class ClockPassesTime(Animation):
|
|||
radians=hour_radians,
|
||||
**rot_kwargs
|
||||
)
|
||||
self.hour_rotation.begin()
|
||||
self.minute_rotation = Rotating(
|
||||
clock.minute_hand,
|
||||
radians=12 * hour_radians,
|
||||
**rot_kwargs
|
||||
)
|
||||
self.minute_rotation.begin()
|
||||
Animation.__init__(self, clock, **kwargs)
|
||||
|
||||
def interpolate_mobject(self, alpha):
|
||||
|
|
|
|||
|
|
@ -2817,18 +2817,16 @@ class ShowTwoPopulations(Scene):
|
|||
num_foxes = Integer(10)
|
||||
num_foxes.scale(self.count_word_scale_val)
|
||||
num_foxes.next_to(labels[0], RIGHT)
|
||||
num_foxes.align_to(labels[0][1], DOWN)
|
||||
num_foxes.align_to(labels[0][0][1], DOWN)
|
||||
num_rabbits = Integer(10)
|
||||
num_rabbits.scale(self.count_word_scale_val)
|
||||
num_rabbits.next_to(labels[1], RIGHT)
|
||||
num_rabbits.align_to(labels[1][1], DOWN)
|
||||
num_rabbits.align_to(labels[1][0][1], DOWN)
|
||||
|
||||
self.add(ContinualChangingDecimal(
|
||||
num_foxes, lambda a: get_num_foxes()
|
||||
))
|
||||
self.add(ContinualChangingDecimal(
|
||||
num_rabbits, lambda a: get_num_rabbits()
|
||||
))
|
||||
num_foxes.add_updater(lambda d: d.set_value(get_num_foxes()))
|
||||
num_rabbits.add_updater(lambda d: d.set_value(get_num_rabbits()))
|
||||
|
||||
self.add(num_foxes, num_rabbits)
|
||||
|
||||
for count in num_foxes, num_rabbits:
|
||||
self.add(Mobject.add_updater(
|
||||
|
|
@ -2853,13 +2851,13 @@ class ShowTwoPopulations(Scene):
|
|||
height=self.animal_height,
|
||||
fill_color=color,
|
||||
)
|
||||
for submob in result.family_members_with_points():
|
||||
if submob.is_subpath:
|
||||
submob.is_subpath = False
|
||||
submob.set_fill(
|
||||
interpolate_color(color, BLACK, 0.8),
|
||||
opacity=1
|
||||
)
|
||||
# for submob in result.family_members_with_points():
|
||||
# if submob.is_subpath:
|
||||
# submob.is_subpath = False
|
||||
# submob.set_fill(
|
||||
# interpolate_color(color, BLACK, 0.8),
|
||||
# opacity=1
|
||||
# )
|
||||
x_shift, y_shift = [
|
||||
(2 * random.random() - 1) * max_val
|
||||
for max_val in [
|
||||
|
|
@ -3021,25 +3019,7 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
|
|||
self.pop_sizes_updates = pop_sizes_updates
|
||||
|
||||
def write_differential_equations(self):
|
||||
variables = ["X", "YY"]
|
||||
equations = TexMobject(
|
||||
"""
|
||||
{dX \\over dt} =
|
||||
X \\cdot (\\alpha - \\beta YY \\,) \\\\
|
||||
\\quad \\\\
|
||||
{dYY \\over dt} =
|
||||
YY \\cdot (\\delta X - \\gamma)
|
||||
""",
|
||||
substrings_to_isolate=variables
|
||||
)
|
||||
animals = [self.get_rabbit(), self.get_fox().flip()]
|
||||
for char, animal in zip(variables, animals):
|
||||
for part in equations.get_parts_by_tex(char):
|
||||
animal_copy = animal.copy()
|
||||
animal_copy.set_height(0.5)
|
||||
animal_copy.move_to(part, DL)
|
||||
Transform(part, animal_copy).update(1)
|
||||
|
||||
equations = self.get_equations()
|
||||
equations.shift(2 * DOWN)
|
||||
rect = SurroundingRectangle(equations, color=YELLOW)
|
||||
rect.set_fill(BLACK, 0.8)
|
||||
|
|
@ -3183,6 +3163,28 @@ class PhaseSpaceOfPopulationModel(ShowTwoPopulations, PiCreatureScene, MovingCam
|
|||
)
|
||||
self.wait(self.flow_time)
|
||||
|
||||
#
|
||||
def get_equations(self):
|
||||
variables = ["X", "YY"]
|
||||
equations = TexMobject(
|
||||
"""
|
||||
{dX \\over dt} =
|
||||
X \\cdot (\\alpha - \\beta YY \\,) \\\\
|
||||
\\quad \\\\
|
||||
{dYY \\over dt} =
|
||||
YY \\cdot (\\delta X - \\gamma)
|
||||
""",
|
||||
substrings_to_isolate=variables
|
||||
)
|
||||
animals = [self.get_rabbit(), self.get_fox().flip()]
|
||||
for char, animal in zip(variables, animals):
|
||||
for part in equations.get_parts_by_tex(char):
|
||||
animal_copy = animal.copy()
|
||||
animal_copy.set_height(0.5)
|
||||
animal_copy.move_to(part, DL)
|
||||
part.become(animal_copy)
|
||||
return equations
|
||||
|
||||
|
||||
class PhaseFlowWords(Scene):
|
||||
def construct(self):
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ def stage_scenes(module_name):
|
|||
# }
|
||||
# TODO, fix this
|
||||
animation_dir = os.path.join(
|
||||
VIDEO_DIR, "clacks_solution2", "1440p60"
|
||||
VIDEO_DIR, "ode", "part1", "1440p60"
|
||||
)
|
||||
#
|
||||
files = os.listdir(animation_dir)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue