From 34851761e294cbb9150f4575b1604e791e5f8c8a Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 28 Mar 2019 16:20:09 -0700 Subject: [PATCH] Many more ode1 scenes and fixes as the video gets edited --- active_projects/ode/all_part1_scenes.py | 22 +- active_projects/ode/part1/pendulum.py | 252 ++++++-- active_projects/ode/part1/phase_space.py | 37 +- active_projects/ode/part1/pi_scenes.py | 60 +- .../ode/part1/shared_constructs.py | 10 + active_projects/ode/part1/staging.py | 588 +++++++++++++++++- active_projects/ode/part1/wordy_scenes.py | 368 ++++++++++- .../ode/solve_pendulum_ode_sample_code.py | 63 ++ stage_scenes.py | 2 +- 9 files changed, 1267 insertions(+), 135 deletions(-) create mode 100644 active_projects/ode/solve_pendulum_ode_sample_code.py diff --git a/active_projects/ode/all_part1_scenes.py b/active_projects/ode/all_part1_scenes.py index 8065580c..83e220c9 100644 --- a/active_projects/ode/all_part1_scenes.py +++ b/active_projects/ode/all_part1_scenes.py @@ -6,8 +6,10 @@ from active_projects.ode.part1.wordy_scenes import * OUTPUT_DIRECTORY = "ode/part1" ALL_SCENE_CLASSES = [ + VectorFieldTest, IntroducePendulum, MultiplePendulumsOverlayed, + PeriodFormula, FormulasAreLies, MediumAnglePendulum, MediumHighAnglePendulum, @@ -18,21 +20,31 @@ ALL_SCENE_CLASSES = [ VeryLowAnglePendulum, FormulasAreLies, TourOfDifferentialEquations, + WherePendulumLeads, + LongDoublePendulum, # FollowThisThread, StrogatzQuote, + ShowHorizontalDashedLine, + RabbitFoxPopulations, + RabbitFoxEquation, # Something... ShowGravityAcceleration, AnalyzePendulumForce, + ShowSineValues, BuildUpEquation, ShowDerivativeVideo, SubtleAirCurrents, + SimpleDampenedPendulum, DefineODE, + SecondOrderEquationExample, ODEvsPDEinFrames, ProveTeacherWrong, SetAsideSeekingSolution, - ReferencePiCollisionStateSpaces, + # VisualizeHeightSlopeCurvature, VisualizeStates, + ReferencePiCollisionStateSpaces, IntroduceVectorField, + XComponentArrows, BreakingSecondOrderIntoTwoFirstOrder, ShowPendulumPhaseFlow, ShowHighVelocityCase, @@ -42,10 +54,18 @@ ALL_SCENE_CLASSES = [ LorenzVectorField, ThreeBodiesInSpace, AltThreeBodiesInSpace, + ThreeBodyTitle, ThreeBodySymbols, AskAboutActuallySolving, WriteODESolvingCode, TakeManyTinySteps, InaccurateComputation, HungerForExactness, + ShowRect, + JumpToThisPoint, + ThreeBodyEquation, + ItGetsWorse, + ChaosTitle, + RevisitQuote, + EndScreen, ] diff --git a/active_projects/ode/part1/pendulum.py b/active_projects/ode/part1/pendulum.py index 5feba2d6..4d8c28b7 100644 --- a/active_projects/ode/part1/pendulum.py +++ b/active_projects/ode/part1/pendulum.py @@ -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,11 +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) - self.wait(4) + + 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 + ) def show_graph_period(self): pendulum = self.pendulum @@ -503,13 +531,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 +559,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 +608,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 +631,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 +639,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 +896,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": { @@ -1215,13 +1332,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) + pendulum_copy.end_swinging() + self.remove(pendulum_copy) + pendulum.remove_updater(new_updater) + self.update_mobjects(0) def show_arc_length(self): pendulum = self.pendulum @@ -1664,6 +1790,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 diff --git a/active_projects/ode/part1/phase_space.py b/active_projects/ode/part1/phase_space.py index baf80d8d..63992f6c 100644 --- a/active_projects/ode/part1/phase_space.py +++ b/active_projects/ode/part1/phase_space.py @@ -951,13 +951,13 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene): self.initialize_vector_field() self.add(self.vector_field) self.add_flexible_state() + self.show_high_vector() self.show_trajectory() - def show_trajectory(self): + def add_flexible_state(self): + super().add_flexible_state() state = self.state plane = self.plane - field = self.vector_field - frame = self.camera_frame state.to_edge(DOWN, buff=SMALL_BUFF), start_point = plane.coords_to_point(0, 4) @@ -965,6 +965,33 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene): dot.move_to(start_point) state.update() + self.dot = dot + self.start_point = start_point + + def show_high_vector(self): + field = self.vector_field + top_vectors = VGroup(*filter( + lambda a: np.all(a.get_center() > [-10, 1.5, -10]), + field + )).copy() + top_vectors.set_stroke(PINK, 3) + top_vectors.sort(lambda p: p[0]) + + self.play( + ShowCreationThenFadeOut( + top_vectors, + run_time=2, + lag_ratio=0.01, + ) + ) + + def show_trajectory(self): + state = self.state + field = self.vector_field + frame = self.camera_frame + dot = self.dot + start_point = self.start_point + traj = VMobject() traj.start_new_path(start_point) dt = 0.01 @@ -998,6 +1025,10 @@ class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene): class TweakMuInFormula(Scene): def construct(self): + self.add(FullScreenFadeRectangle( + opacity=0.75, + )) + ode = get_ode() ode.to_edge(DOWN, buff=LARGE_BUFF) mu = ode.get_part_by_tex("\\mu") diff --git a/active_projects/ode/part1/pi_scenes.py b/active_projects/ode/part1/pi_scenes.py index f3d304a3..4c753585 100644 --- a/active_projects/ode/part1/pi_scenes.py +++ b/active_projects/ode/part1/pi_scenes.py @@ -111,7 +111,11 @@ class FormulasAreLies(PiCreatureScene): 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 +123,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) @@ -376,9 +381,44 @@ 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) + + self.play( + FadeInFromDown(mystery), + self.teacher.change, "pondering" + ) + self.add( + AnimatedBoundary(mystery, stroke_width=1), + mystery, + ) + self.change_all_student_modes("sad") + self.look_at(mystery) + self.wait(5) + + +class ItGetsWorse(TeacherStudentsScene): + def construct(self): + self.teacher_says("It gets\\\\worse") + self.change_student_modes( + "hesitant", "pleading", "erm" + ) + self.wait(2) diff --git a/active_projects/ode/part1/shared_constructs.py b/active_projects/ode/part1/shared_constructs.py index 265a4585..e803f694 100644 --- a/active_projects/ode/part1/shared_constructs.py +++ b/active_projects/ode/part1/shared_constructs.py @@ -35,6 +35,16 @@ def get_ode(): return ode +def get_period_formula(): + return TexMobject( + "\\sqrt{\\,", "2\\pi", "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([ diff --git a/active_projects/ode/part1/staging.py b/active_projects/ode/part1/staging.py index 8fb884da..7cda089e 100644 --- a/active_projects/ode/part1/staging.py +++ b/active_projects/ode/part1/staging.py @@ -3,6 +3,8 @@ from active_projects.ode.part1.shared_constructs import * from active_projects.ode.part1.pendulum import Pendulum from active_projects.ode.part1.pendulum import ThetaVsTAxes from active_projects.ode.part1.phase_space import IntroduceVectorField +from old_projects.div_curl import PhaseSpaceOfPopulationModel +from old_projects.div_curl import ShowTwoPopulations # Scenes @@ -15,16 +17,21 @@ class VectorFieldTest(Scene): ) mu_tracker = ValueTracker(1) field = VectorField( - lambda p: pendulum_vector_field( + lambda p: pendulum_vector_field_func( plane.point_to_coords(p), mu=mu_tracker.get_value() ), delta_x=0.5, delta_y=0.5, - max_magnitude=4, + max_magnitude=6, opacity=0.5, # length_func=lambda norm: norm, ) + field.set_opacity(1) + + self.add(plane, field) + return + stream_lines = StreamLines( field.func, delta_x=0.5, @@ -39,6 +46,26 @@ class VectorFieldTest(Scene): self.wait(10) +class ShowRect(Scene): + def construct(self): + rect = Rectangle() + rect.set_stroke(YELLOW) + rect.set_height(1) + rect.set_width(3, stretch=True) + self.play(ShowCreation(rect)) + self.play(FadeOut(rect)) + + +class PeriodFormula(Scene): + def construct(self): + formula = get_period_formula() + formula.scale(2) + q_mark = TexMobject("?") + q_mark.scale(3) + q_mark.next_to(formula, RIGHT) + self.add(formula, q_mark) + + class TourOfDifferentialEquations(MovingCameraScene): CONFIG = { "screen_rect_style": { @@ -214,13 +241,32 @@ class HeatEquationPreview(ExternallyAnimatedScene): pass +class ShowHorizontalDashedLine(Scene): + def construct(self): + line = DashedLine(LEFT_SIDE, RIGHT_SIDE) + self.play(ShowCreation(line)) + self.wait() + + +class RabbitFoxPopulations(ShowTwoPopulations): + pass + + +class RabbitFoxEquation(PhaseSpaceOfPopulationModel): + def construct(self): + equations = self.get_equations() + self.add(equations) + + class ShowGravityAcceleration(Scene): def construct(self): self.add_gravity_field() self.add_title() self.pulse_gravity_down() + self.show_g_value() self.show_trajectory() self.combine_v_vects() + self.show_g_symbol() def add_gravity_field(self): gravity_field = self.gravity_field = VectorField( @@ -238,17 +284,11 @@ class ShowGravityAcceleration(Scene): title = self.title = TextMobject("Gravitational acceleration") title.scale(1.5) title.to_edge(UP) - g_eq = self.g_eq = TexMobject( - "{g}", "=", "-9.8", "\\frac{\\text{m/s}}{\\text{s}}", - **Lg_formula_config + title.add_background_rectangle( + buff=0.05, + opacity=1, ) - g_eq.next_to(title, DOWN) - for mob in title, g_eq: - mob.add_background_rectangle_to_submobjects( - buff=0.05, - opacity=1, - ) - self.add(title, g_eq) + self.play(FadeInFromDown(title)) def pulse_gravity_down(self): field = self.gravity_field @@ -260,7 +300,36 @@ class ShowGravityAcceleration(Scene): ) for vector in field ]), run_time=2, lag_ratio=0.001) - self.add(self.title, self.g_eq) + self.add(self.title) + + def show_g_value(self): + title = self.title + g_eq = self.g_eq = TexMobject( + "-9.8", "{\\text{m/s}", "\\over", "\\text{s}}", + **Lg_formula_config + ) + g_eq.add_background_rectangle_to_submobjects() + g_eq.scale(2) + g_eq.center() + num, ms, per, s = g_eq + + self.add(num) + self.wait(0.75) + self.play( + FadeInFrom(ms, 0.25 * DOWN, run_time=0.5) + ) + self.wait(0.25) + self.play(LaggedStart( + GrowFromPoint(per, per.get_left()), + FadeInFrom(s, 0.5 * UP), + lag_ratio=0.7, + run_time=0.75 + )) + self.wait() + self.play( + g_eq.scale, 0.5, + g_eq.next_to, title, DOWN, + ) def show_trajectory(self): ball = Circle( @@ -329,6 +398,18 @@ class ShowGravityAcceleration(Scene): rate_func=squish_rate_func(smooth, 0, 0.1) ) + time_label = TextMobject("Time = ") + time_label.shift(MED_SMALL_BUFF * LEFT) + time_tracker = ValueTracker(0) + time = DecimalNumber(0) + time.next_to(time_label, RIGHT) + time.add_updater(lambda d, dt: d.set_value( + time_tracker.get_value() + )) + time_group = VGroup(time_label, time) + time_group.center().to_edge(DOWN) + self.add(time_group) + ball_copies = VGroup() v_vect_copies = VGroup() self.add(dashed_graph, ball) @@ -353,11 +434,16 @@ class ShowGravityAcceleration(Scene): ShowCreation(dashed_graph, **kw), MoveAlongPath(ball, graph, **kw), MoveAlongPath(v_point, velocity_graph, **kw), + ApplyMethod( + time_tracker.increment_value, 1, + rate_func=linear + ), flash, run_time=1, ) dashed_graph.restore() randy.clear_updaters() + self.play(FadeOut(time_group)) self.wait() self.v_vects = v_vect_copies @@ -406,16 +492,30 @@ class ShowGravityAcceleration(Scene): ) self.wait() + def show_g_symbol(self): + g = TexMobject("g") + brace = Brace(self.g_eq[0][2:], UP, buff=SMALL_BUFF) + g.scale(1.5) + g.next_to(brace, UP) + g.set_color(YELLOW) + self.play( + FadeOut(self.title), + GrowFromCenter(brace), + FadeInFrom(g, UP), + ) + self.wait() + class ShowDerivativeVideo(Scene): - CONFIG = { - "camera_config": {"background_color": DARKER_GREY} - } - def construct(self): - title = TextMobject("Essence of Calculus") - title.scale(1.25) + title = TextMobject("Essence of", "Calculus") + title.scale(1.5) title.to_edge(UP) + + title2 = TextMobject("Essence of", "Linear Algebra") + title2.scale(1.5) + title2.move_to(title, DOWN) + rect = ScreenRectangle(height=6) rect = rect.copy() rect.set_style( @@ -428,6 +528,8 @@ class ShowDerivativeVideo(Scene): self.add(title, rect) self.add(animated_rect) + self.wait(5) + self.play(ReplacementTransform(title, title2)) self.wait(10) @@ -483,7 +585,7 @@ class DefineODE(Scene): def write_differential_equation(self): de_word = TextMobject("Differential", "Equation") - de_word.to_edge(UP) + de_word.to_edge(UP, buff=MED_SMALL_BUFF) equation = get_ode() equation.next_to(de_word, DOWN) @@ -777,11 +879,7 @@ class DefineODE(Scene): self.second_order_word = so def show_higher_order_examples(self): - main_example = VGroup( - self.second_order_word, - self.ode_initials, - self.equation - ) + main_example = self.get_main_example() tex_config = {"tex_to_color_map": {"{x}": BLUE}} example3 = VGroup( TextMobject("Third order ODE"), @@ -821,6 +919,13 @@ class DefineODE(Scene): ) self.wait(2) + def get_main_example(self): + return VGroup( + self.second_order_word, + self.ode_initials, + self.equation + ) + def show_changing_curvature_group(self): t_tracker = self.t_tracker curvature_group = self.curvature_group @@ -828,7 +933,8 @@ class DefineODE(Scene): graph = VMobject() graph.pointwise_become_partial( self.fake_graph, - 0.25, 1, + t_tracker.get_value() / self.axes.x_max, + 1, ) dashed_graph = DashedVMobject(graph, num_dashes=100) dashed_graph.set_stroke(GREEN, 1) @@ -844,14 +950,397 @@ class DefineODE(Scene): self.wait() -class ODEvsPDEinFrames(Scene): +# Largely a copy of DefineODE, which is not great. +# But what can you do? +class SecondOrderEquationExample(DefineODE): def construct(self): - pass + self.add_graph() + self.write_differential_equation() + self.show_value_slope_curvature() + self.show_higher_order_examples() + self.show_changing_curvature_group() + + def add_graph(self): + axes = self.axes = Axes( + x_min=0, + x_max=10.5, + y_min=-2.5, + y_max=2.5, + ) + axes.center() + axes.to_edge(DOWN) + x_t = TexMobject("x", "(t)") + x_t.set_color_by_tex("x", BLUE) + t = TexMobject("t") + t.next_to(axes.x_axis.get_right(), UP) + x_t.next_to(axes.y_axis.get_top(), UP) + + axes.add(t, x_t) + axes.add_coordinates() + + self.add(axes) + + def write_differential_equation(self): + de_word = TextMobject("Differential", "Equation") + de_word.scale(1.25) + de_word.to_edge(UP, buff=MED_SMALL_BUFF) + so_word = TextMobject("Second Order") + so_word.scale(1.25) + de_word.generate_target() + group = VGroup(so_word, de_word.target) + group.arrange(RIGHT) + group.to_edge(UP, buff=MED_SMALL_BUFF) + so_word.align_to(de_word.target[0], DOWN) + so_line = Line(LEFT, RIGHT, color=YELLOW) + so_line.match_width(so_word) + so_line.next_to(so_word, DOWN, buff=SMALL_BUFF) + + equation = TexMobject( + "{\\ddot x}(t)", "=", + "-\\mu", "{\\dot x}(t)", + "-", "\\omega", "{x}(t)", + tex_to_color_map={ + "{x}": BLUE, + "{\\dot x}": YELLOW, + "{\\ddot x}": RED, + } + ) + equation.next_to(de_word, DOWN) + + dd_x_part = equation[:2] + dd_x_rect = SurroundingRectangle(dd_x_part) + + self.add(de_word, equation) + self.play( + MoveToTarget(de_word), + FadeInFrom(so_word, RIGHT), + GrowFromCenter(so_line), + ) + self.play(ReplacementTransform(so_line, dd_x_rect)) + self.play(FadeOut(dd_x_rect)) + + self.equation = equation + self.title = VGroup(*so_word, *de_word) + + def show_value_slope_curvature(self): + axes = self.axes + graph = axes.get_graph( + lambda t: -2.5 * np.cos(2 * t) * np.exp(-0.2 * t) + ) + + tex_config = { + "tex_to_color_map": { + "{x}": BLUE, + "{\\dot x}": YELLOW, + "{\\ddot x}": RED, + }, + "height": 0.5, + } + x, d_x, dd_x = [ + TexMobject( + "{" + s + "x}(t)", + **tex_config + ) + for s in ("", "\\dot ", "\\ddot ") + ] + + t_tracker = ValueTracker(1.25) + get_t = t_tracker.get_value + + def get_point(t): + return graph.point_from_proportion(t / axes.x_max) + + def get_dot(): + return Dot(get_point(get_t())).scale(0.5) + + def get_v_line(): + point = get_point(get_t()) + x_point = axes.x_axis.number_to_point( + axes.x_axis.point_to_number(point) + ) + return DashedLine( + x_point, point, + dash_length=0.025, + stroke_color=BLUE, + stroke_width=2, + ) + + def get_tangent_line(curve, alpha): + line = Line( + ORIGIN, 1.5 * RIGHT, + color=YELLOW, + stroke_width=1.5, + ) + da = 0.0001 + p0 = curve.point_from_proportion(alpha) + p1 = curve.point_from_proportion(alpha - da) + p2 = curve.point_from_proportion(alpha + da) + angle = angle_of_vector(p2 - p1) + line.rotate(angle) + line.move_to(p0) + return line + + def get_slope_line(): + return get_tangent_line( + graph, get_t() / axes.x_max + ) + + def get_curve(): + curve = VMobject() + t = get_t() + curve.set_points_smoothly([ + get_point(t + a) + for a in np.linspace(-0.5, 0.5, 11) + ]) + curve.set_stroke(RED, 1) + return curve + + v_line = always_redraw(get_v_line) + dot = always_redraw(get_dot) + slope_line = always_redraw(get_slope_line) + curve = always_redraw(get_curve) + + x.next_to(v_line, RIGHT, SMALL_BUFF) + d_x.next_to(slope_line.get_end(), UP, SMALL_BUFF) + dd_x.next_to(curve.get_end(), RIGHT, SMALL_BUFF) + xs = VGroup(x, d_x, dd_x) + + words = VGroup( + TextMobject("= Height").set_color(BLUE), + TextMobject("= Slope").set_color(YELLOW), + TextMobject("= ``Curvature''").set_color(RED), + ) + words.scale(0.75) + for word, sym in zip(words, xs): + word.next_to(sym, RIGHT, buff=2 * SMALL_BUFF) + sym.word = word + + self.play( + ShowCreation(v_line), + FadeInFromPoint(dot, v_line.get_start()), + FadeInFrom(x, DOWN), + FadeInFrom(x.word, DOWN), + ) + self.add(slope_line, dot) + self.play( + ShowCreation(slope_line), + FadeInFrom(d_x, LEFT), + FadeInFrom(d_x.word, LEFT), + ) + + a_tracker = ValueTracker(0) + curve_copy = curve.copy() + changing_slope = always_redraw( + lambda: get_tangent_line( + curve_copy, + a_tracker.get_value(), + ).set_stroke( + opacity=there_and_back(a_tracker.get_value()) + ) + ) + self.add(curve, dot) + self.play( + ShowCreation(curve), + FadeInFrom(dd_x, LEFT), + FadeInFrom(dd_x.word, LEFT), + ) + self.add(changing_slope) + self.play( + a_tracker.set_value, 1, + run_time=3, + ) + self.remove(changing_slope, a_tracker) + + self.t_tracker = t_tracker + self.curvature_group = VGroup( + v_line, slope_line, curve, dot + ) + self.curvature_group_labels = VGroup(xs, words) + self.fake_graph = graph + + def get_main_example(self): + return VGroup( + self.equation, + self.title, + ) + +# class VisualizeHeightSlopeCurvature(DefineODE): +# CONFIG = { +# "pendulum_config": { +# "length": 2, +# "top_point": 5 * RIGHT + 2 * UP, +# "initial_theta": 175 * DEGREES, +# "mu": 0.3, +# }, +# } + +# def construct(self): +# self.add_graph() +# self.show_value_slope_curvature() +# self.show_changing_curvature_group() + + +class ODEvsPDEinFrames(Scene): + CONFIG = { + "camera_config": {"background_color": DARKER_GREY} + } + + def construct(self): + frames = VGroup(*[ + ScreenRectangle( + height=3.5, + fill_opacity=1, + fill_color=BLACK, + stroke_width=0, + ) + for x in range(2) + ]) + frames.arrange(RIGHT, buff=LARGE_BUFF) + frames.shift(0.5 * DOWN) + + animated_frames = VGroup(*[ + AnimatedBoundary( + frame, + cycle_rate=0.2, + max_stroke_width=1, + ) + for frame in frames + ]) + + titles = VGroup( + # TextMobject("ODEs"), + # TextMobject("PDEs"), + TextMobject("Ordinary", "Differential", "Equations"), + TextMobject("Partial", "Differential", "Equations"), + ) + for title, frame in zip(titles, frames): + title.arrange( + DOWN, + buff=MED_SMALL_BUFF, + aligned_edge=LEFT + ) + title.next_to(frame, UP, MED_LARGE_BUFF) + title.initials = VGroup(*[ + part[0] for part in title + ]) + titles[0][1].shift(0.05 * UP) + + # ODE content + ode = get_ode() + ode.set_width(frames[0].get_width() - MED_LARGE_BUFF) + ode.next_to(frames[0].get_top(), DOWN) + ts = ode.get_parts_by_tex("{t}") + one_input = TextMobject("One input") + one_input.next_to(frames[0].get_bottom(), UP) + o_arrows = VGroup(*[ + Arrow( + one_input.get_top(), + t.get_bottom(), + buff=0.2, + color=WHITE, + max_tip_length_to_length_ratio=0.075, + path_arc=pa + ) + for t, pa in zip(ts, [-0.7, 0, 0.7]) + ]) + o_arrows.set_stroke(width=3) + frames[0].add(ode, one_input, o_arrows) + + # PDE content + pde = TexMobject( + """ + \\frac{\\partial T}{\\partial t} + {(x, y, t)} = + \\frac{\\partial^2 T}{\\partial x^2} + {(x, y, t)} + + \\frac{\\partial^2 T}{\\partial y^2} + {(x, y, t)} + """, + tex_to_color_map={"{(x, y, t)}": WHITE} + ) + pde.set_width(frames[1].get_width() - MED_LARGE_BUFF) + pde.next_to(frames[1].get_top(), DOWN) + inputs = pde.get_parts_by_tex("{(x, y, t)}") + multi_input = TextMobject("Multiple inputs") + multi_input.next_to(frames[1].get_bottom(), UP) + p_arrows = VGroup(*[ + Arrow( + multi_input.get_top(), + mob.get_bottom(), + buff=0.2, + color=WHITE, + max_tip_length_to_length_ratio=0.075, + path_arc=pa + ) + for mob, pa in zip(inputs, [-0.7, 0, 0.7]) + ]) + p_arrows.set_stroke(width=3) + frames[1].add(pde, multi_input, p_arrows) + + self.add( + frames[0], + animated_frames[0], + titles[0] + ) + self.play( + Write(one_input), + ShowCreation(o_arrows, lag_ratio=0.5) + ) + self.wait(2) + self.play(titles[0].initials.set_color, BLUE) + self.wait(7) + + # Transition + self.play( + TransformFromCopy(*titles), + TransformFromCopy(*frames), + ) + self.play(VFadeIn(animated_frames[1])) + self.wait() + self.play(titles[1].initials.set_color, YELLOW) + self.wait(30) class ReferencePiCollisionStateSpaces(Scene): + CONFIG = { + "camera_config": {"background_color": DARKER_GREY} + } + def construct(self): - pass + title = TextMobject("The block collision puzzle") + title.scale(1.5) + title.to_edge(UP) + self.add(title) + + frames = VGroup(*[ + ScreenRectangle( + height=3.5, + fill_opacity=1, + fill_color=BLACK, + stroke_width=0, + ) + for x in range(2) + ]) + frames.arrange(RIGHT, buff=LARGE_BUFF) + boundary = AnimatedBoundary(frames) + self.add(frames, boundary) + self.wait(15) + + +class XComponentArrows(Scene): + def construct(self): + field = VectorField( + lambda p: np.array([p[1], 0, 0]) + ) + field.set_opacity(0.75) + for u in (1, -1): + field.sort(lambda p: u * p[0]) + self.play(LaggedStartMap( + GrowArrow, field, + lag_ratio=0.1, + run_time=1 + )) + self.play(FadeOut(field)) class BreakingSecondOrderIntoTwoFirstOrder(IntroduceVectorField): @@ -879,6 +1368,14 @@ class BreakingSecondOrderIntoTwoFirstOrder(IntroduceVectorField): system1.next_to(sys_word, DOWN) system2.move_to(system1) + theta_dots = VGroup(*filter( + lambda m: ( + isinstance(m, SingleStringTexMobject) and + "{\\dot\\theta}" == m.get_tex_string() + ), + system1.get_family(), + )) + self.add(ode) self.play(FadeInFrom(so_word, 0.5 * DOWN)) self.wait() @@ -905,6 +1402,13 @@ class BreakingSecondOrderIntoTwoFirstOrder(IntroduceVectorField): FadeInFromDown(sys_word) ) self.wait() + self.play(LaggedStartMap( + ShowCreationThenFadeAround, + theta_dots, + surrounding_rectangle_config={ + "color": PINK, + } + )) self.play(ReplacementTransform(system1, system2)) self.wait() @@ -993,6 +1497,13 @@ class ThreeBodiesInSpace(SpecialThreeDScene): axes.set_stroke(width=0.5) self.add(axes) + # Orient + self.set_camera_orientation( + phi=70 * DEGREES, + theta=-110 * DEGREES, + ) + self.begin_ambient_camera_rotation() + def add_bodies(self): masses = self.masses colors = self.colors @@ -1071,11 +1582,6 @@ class ThreeBodiesInSpace(SpecialThreeDScene): self.add(traj, body) def let_play(self): - self.set_camera_orientation( - phi=70 * DEGREES, - theta=-110 * DEGREES, - ) - self.begin_ambient_camera_rotation() # Break it up to see partial files as # it's rendered for x in range(int(self.play_time)): @@ -1127,6 +1633,20 @@ class AltThreeBodiesInSpace(ThreeBodiesInSpace): } +class TwoBodiesInSpace(ThreeBodiesInSpace): + CONFIG = { + "colors": [GREY, BLUE], + "masses": [1, 6], + "play_time": 5, + } + + def construct(self): + self.add_axes() + self.add_bodies() + self.add_trajectories() + self.let_play() + + class DefineODECopy(DefineODE): pass diff --git a/active_projects/ode/part1/wordy_scenes.py b/active_projects/ode/part1/wordy_scenes.py index 6a966789..8ada4c57 100644 --- a/active_projects/ode/part1/wordy_scenes.py +++ b/active_projects/ode/part1/wordy_scenes.py @@ -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,46 +85,58 @@ 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) + + return quote + + +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) - movers = VGroup(*quote[:-1].family_members_with_points()) - for mover in movers: - mover.save_state() - disc = Circle(radius=0.05) - disc.set_stroke(width=0) - disc.set_fill(BLACK, 0) - disc.move_to(mover) - mover.become(disc) self.play( - FadeInFrom(author_part, LEFT), - LaggedStartMap( - # FadeInFromLarge, - # quote[:-1].family_members_with_points(), - Restore, movers, - lag_ratio=0.005, - run_time=2, - ) - # FadeInFromDown(quote[:-1]), - # lag_ratio=0.01, + angle_tracker.set_value, 0, + run_time=3, ) self.wait() self.play( - Write(law_part.copy().set_color(YELLOW)), - run_time=1, + angle_tracker.set_value, 90 * DEGREES, + run_time=3, ) self.wait() - self.play( - Write(language_part.copy().set_color(BLUE)), - run_time=1.5, + + def get_sine_formula(self, angle): + sin, lp, rp = TexMobject( + "\\sin", "(", ") = " ) - self.wait(2) + 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): @@ -141,6 +187,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 +314,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 theorey") + 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", + ] + } diff --git a/active_projects/ode/solve_pendulum_ode_sample_code.py b/active_projects/ode/solve_pendulum_ode_sample_code.py new file mode 100644 index 00000000..e7076c3c --- /dev/null +++ b/active_projects/ode/solve_pendulum_ode_sample_code.py @@ -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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stage_scenes.py b/stage_scenes.py index f993081e..ab84fe49 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -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)