From 8681e18c42e85a8000cd1e654592828d902a0e44 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 20 Mar 2019 17:38:25 -0700 Subject: [PATCH] More ode part1 scenes --- active_projects/ode/all_part1_scenes.py | 8 +- active_projects/ode/part1/pendulum.py | 62 ++--- active_projects/ode/part1/pi_scenes.py | 108 ++++++++ .../ode/part1/shared_constructs.py | 16 ++ active_projects/ode/part1/staging.py | 247 ++++++++++++------ 5 files changed, 326 insertions(+), 115 deletions(-) create mode 100644 active_projects/ode/part1/pi_scenes.py create mode 100644 active_projects/ode/part1/shared_constructs.py diff --git a/active_projects/ode/all_part1_scenes.py b/active_projects/ode/all_part1_scenes.py index 68bf3fe4..4f071177 100644 --- a/active_projects/ode/all_part1_scenes.py +++ b/active_projects/ode/all_part1_scenes.py @@ -1,15 +1,21 @@ from active_projects.ode.part1.pendulum import * from active_projects.ode.part1.staging import * +from active_projects.ode.part1.pi_scenes import * OUTPUT_DIRECTORY = "ode/part1" ALL_SCENE_CLASSES = [ IntroducePendulum, MultiplePendulumsOverlayed, - # FormulasAreLies, + FormulasAreLies, MediumAnglePendulum, MediumHighAnglePendulum, HighAnglePendulum, LowAnglePendulum, + SomeOfYouWatching, + SmallAngleApproximationTex, + VeryLowAnglePendulum, + FollowThisThread, + StrogatzQuote, # Tests PendulumTest, VectorFieldTest, diff --git a/active_projects/ode/part1/pendulum.py b/active_projects/ode/part1/pendulum.py index ce70d6be..e61958a0 100644 --- a/active_projects/ode/part1/pendulum.py +++ b/active_projects/ode/part1/pendulum.py @@ -1,13 +1,5 @@ from big_ol_pile_of_manim_imports import * - - -Lg_formula_config = { - "tex_to_color_map": { - "\\theta_0": WHITE, - "{L}": BLUE, - "{g}": YELLOW, - }, -} +from active_projects.ode.part1.shared_constructs import * class Pendulum(VGroup): @@ -828,36 +820,30 @@ class HighAnglePendulum(LowAnglePendulum): } -class PendulumTest(Scene): +class VeryLowAnglePendulum(LowAnglePendulum): + CONFIG = { + "pendulum_config": { + "initial_theta": 10 * DEGREES, + "n_steps_per_frame": 1000, + "top_point": ORIGIN, + "length": 3, + }, + "axes_config": { + "y_axis_config": {"unit_size": 2}, + "y_max": PI / 4, + "y_min": -PI / 4, + "number_line_config": { + "tip_length": 0.3, + "stroke_width": 2, + } + }, + "pendulum_shift_vect": 1 * RIGHT, + } + + +class BuildUpEquation(Scene): def construct(self): - pendulum = Pendulum( - initial_theta=170 * DEGREES, - top_point=ORIGIN, - damping=0 - ) - pendulum.add_velocity_vector() - pendulum.add_theta_label() - - gravity_vector = GravityVector(pendulum) - gravity_vector.add_component_lines() - - axes = ThetaVsTAxes( - y_max=PI, - y_min=-PI, - y_axis_config={ - "unit_size": 0.7, - "tick_frequency": PI / 4, - }, - x_max=14 - ) - axes.to_corner(UL) - axes.add_live_drawn_graph(pendulum) - - self.add(pendulum, gravity_vector) - self.add(axes) - - pendulum.start_swinging() - self.wait(14) + pass class NewSceneName(Scene): diff --git a/active_projects/ode/part1/pi_scenes.py b/active_projects/ode/part1/pi_scenes.py new file mode 100644 index 00000000..4c53a9d6 --- /dev/null +++ b/active_projects/ode/part1/pi_scenes.py @@ -0,0 +1,108 @@ +from big_ol_pile_of_manim_imports import * +from active_projects.ode.part1.shared_constructs import * + + +class SomeOfYouWatching(TeacherStudentsScene): + CONFIG = { + "camera_config": { + "background_color": DARKER_GREY, + } + } + + def construct(self): + screen = self.screen + screen.scale(1.25, about_edge=UL) + screen.set_fill(BLACK, 1) + self.add(screen) + + self.teacher.change("raise_right_hand") + for student in self.students: + student.change("pondering", screen) + + self.student_says( + "Well...yeah", + target_mode="tease" + ) + self.wait(3) + + +class FormulasAreLies(PiCreatureScene): + def construct(self): + you = self.pi_creature + t2c = { + "{L}": BLUE, + "{g}": YELLOW, + "\\theta_0": WHITE, + "\\sqrt{\\,": WHITE, + } + kwargs = {"tex_to_color_map": t2c} + period_eq = TexMobject( + "\\text{Period} = 2\\pi \\sqrt{\\,{L} / {g}}", + **kwargs + ) + theta_eq = TexMobject( + "\\theta(t) = \\theta_0 \\cos\\left(" + "\\sqrt{\\,{L} / {g}} \\cdot t" + "\\right)", + **kwargs + ) + equations = VGroup(theta_eq, period_eq) + equations.arrange(DOWN, buff=LARGE_BUFF) + + for eq in period_eq, theta_eq: + i = eq.index_of_part_by_tex("\\sqrt") + eq.sqrt_part = eq[i:i + 4] + + theta0 = theta_eq.get_part_by_tex("\\theta_0") + theta0_words = TextMobject("Starting angle") + theta0_words.next_to(theta0, UL) + theta0_words.shift(UP + 0.5 * RIGHT) + arrow = Arrow( + theta0_words.get_bottom(), + theta0, + color=WHITE, + tip_length=0.25, + ) + + bubble = SpeechBubble() + bubble.pin_to(you) + bubble.write("Lies!") + bubble.content.scale(2) + bubble.resize_to_content() + + self.add(period_eq) + you.change("pondering", period_eq) + self.wait() + theta_eq.remove(*theta_eq.sqrt_part) + self.play( + TransformFromCopy( + period_eq.sqrt_part, + theta_eq.sqrt_part, + ), + FadeIn(theta_eq) + ) + theta_eq.add(*theta_eq.sqrt_part) + self.play( + FadeInFrom(theta0_words, LEFT), + GrowArrow(arrow), + ) + self.wait() + self.play(you.change, "confused") + self.wait(0) + self.play( + you.change, "angry", + ShowCreation(bubble), + FadeInFromPoint(bubble.content, you.mouth), + equations.to_edge, LEFT, + FadeOut(arrow), + FadeOut(theta0_words), + ) + self.wait() + + def create_pi_creature(self): + return You().flip().to_corner(DR) + + +class NewSceneName(Scene): + def construct(self): + pass diff --git a/active_projects/ode/part1/shared_constructs.py b/active_projects/ode/part1/shared_constructs.py new file mode 100644 index 00000000..606c00b6 --- /dev/null +++ b/active_projects/ode/part1/shared_constructs.py @@ -0,0 +1,16 @@ +from big_ol_pile_of_manim_imports import * + + +Lg_formula_config = { + "tex_to_color_map": { + "\\theta_0": WHITE, + "{L}": BLUE, + "{g}": YELLOW, + }, +} + + +class You(PiCreature): + CONFIG = { + "color": BLUE_C, + } diff --git a/active_projects/ode/part1/staging.py b/active_projects/ode/part1/staging.py index e8d3cb89..8c2d6334 100644 --- a/active_projects/ode/part1/staging.py +++ b/active_projects/ode/part1/staging.py @@ -1,4 +1,5 @@ from big_ol_pile_of_manim_imports import * +from active_projects.ode.part1.shared_constructs import * def pendulum_vector_field(point, mu=0.1, g=9.8, L=3): @@ -44,83 +45,177 @@ class VectorFieldTest(Scene): self.wait(10) -class FormulasAreLies(PiCreatureScene): +class SmallAngleApproximationTex(Scene): def construct(self): - morty = self.pi_creature - t2c = { - "{L}": BLUE, - "{g}": YELLOW, - "\\theta_0": WHITE, - "\\sqrt{\\,": WHITE, + approx = TexMobject( + "\\sin", "(", "\\theta", ") \\approx \\theta", + tex_to_color_map={"\\theta": RED}, + arg_separator="", + ) + + implies = TexMobject("\\Downarrow") + period = TexMobject( + "\\text{Period}", "\\approx", + "2\\pi \\sqrt{\\,{L} / {g}}", + **Lg_formula_config, + ) + group = VGroup(approx, implies, period) + group.arrange(DOWN) + + approx_brace = Brace(approx, UP, buff=SMALL_BUFF) + approx_words = TextMobject( + "For small $\\theta$", + tex_to_color_map={"$\\theta$": RED}, + ) + approx_words.scale(0.75) + approx_words.next_to(approx_brace, UP, SMALL_BUFF) + + self.add(approx, approx_brace, approx_words) + self.play( + Write(implies), + FadeInFrom(period, LEFT) + ) + self.wait() + + +class FollowThisThread(Scene): + CONFIG = { + "screen_rect_style": { + "stroke_width": 2, + "stroke_color": WHITE, + "fill_opacity": 1, + "fill_color": DARKER_GREY, } - kwargs = {"tex_to_color_map": t2c} - period_eq = TexMobject( - "\\text{Period} = 2\\pi \\sqrt{\\,{L} / {g}}", - **kwargs - ) - theta_eq = TexMobject( - "\\theta(t) = \\theta_0 \\cos\\left(" - "\\sqrt{\\,{L} / {g}} \\cdot t" - "\\right)", - **kwargs - ) - equations = VGroup(theta_eq, period_eq) - equations.arrange(DOWN, buff=LARGE_BUFF) + } - for eq in period_eq, theta_eq: - i = eq.index_of_part_by_tex("\\sqrt") - eq.sqrt_part = eq[i:i + 4] - - theta0 = theta_eq.get_part_by_tex("\\theta_0") - theta0_words = TextMobject("Starting angle") - theta0_words.next_to(theta0, UL) - theta0_words.shift(UP + 0.5 * RIGHT) - arrow = Arrow( - theta0_words.get_bottom(), - theta0, - color=WHITE, - tip_length=0.25, - ) - - bubble = SpeechBubble() - bubble.pin_to(morty) - bubble.write("Lies!") - bubble.content.scale(2) - bubble.resize_to_content() - - self.add(period_eq) - morty.change("pondering", period_eq) - self.wait() - theta_eq.remove(*theta_eq.sqrt_part) - self.play( - TransformFromCopy( - period_eq.sqrt_part, - theta_eq.sqrt_part, - ), - FadeIn(theta_eq) - ) - theta_eq.add(*theta_eq.sqrt_part) - self.play( - FadeInFrom(theta0_words, LEFT), - GrowArrow(arrow), - ) - self.wait() - self.play(morty.change, "confused") - self.wait(0) - self.play( - morty.change, "angry", - ShowCreation(bubble), - FadeInFromPoint(bubble.content, morty.mouth), - equations.to_edge, LEFT, - FadeOut(arrow), - FadeOut(theta0_words), - ) - self.wait() - - def create_pi_creature(self): - return Mortimer().to_corner(DR) - - -class NewSceneName(Scene): def construct(self): - pass + self.show_thumbnails() + self.show_words() + + def show_thumbnails(self): + # TODO, replace each of these with a picture? + thumbnails = self.thumbnails = VGroup( + ScreenRectangle(**self.screen_rect_style), + ScreenRectangle(**self.screen_rect_style), + ScreenRectangle(**self.screen_rect_style), + ScreenRectangle(**self.screen_rect_style), + ScreenRectangle(**self.screen_rect_style), + ) + n = len(thumbnails) + thumbnails.set_height(1.5) + + line = self.line = CubicBezier([ + [-5, 3, 0], + [3, 3, 0], + [-3, -3, 0], + [5, -3, 0], + ]) + for thumbnail, a in zip(thumbnails, np.linspace(0, 1, n)): + thumbnail.move_to(line.point_from_proportion(a)) + + self.play( + ShowCreation( + line, + rate_func=lambda t: np.clip(t * (n + 1) / n, 0, 1) + ), + LaggedStart(*[ + GrowFromCenter( + thumbnail, + rate_func=squish_rate_func( + smooth, + 0, 0.7, + ) + ) + for thumbnail in thumbnails + ], lag_ratio=1), + run_time=5 + ) + + def show_words(self): + words = VGroup( + TextMobject("Generalize"), + TextMobject("Put in context"), + TextMobject("Modify"), + ) + # words.arrange(DOWN, aligned_edge=LEFT, buff=LARGE_BUFF) + words.scale(1.5) + words.to_corner(UR) + words.add_to_back(VectorizedPoint(words.get_center())) + words.add(VectorizedPoint(words.get_center())) + + diffEq = TextMobject("Differential\\\\equations") + diffEq.scale(1.5) + diffEq.to_corner(DL, buff=LARGE_BUFF) + + for word1, word2 in zip(words, words[1:]): + self.play( + FadeInFromDown(word2), + FadeOutAndShift(word1, UP), + ) + self.wait() + self.play( + ReplacementTransform( + VGroup(self.thumbnails).copy().fade(1), + diffEq, + lag_ratio=0.01, + ) + ) + self.wait() + + +class StrogatzQuote(Scene): + def construct(self): + law_words = "laws of physics" + language_words = "language of differential equations" + author = "-Steven Strogatz" + quote = TextMobject( + """ + \\Large + ``Since Newton, mankind has come to realize + that the laws of physics are always expressed + in the language of differential equations.''\\\\ + """ + author, + alignment="", + 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.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) + + 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, + ) + self.wait() + self.play( + Write(law_part.copy().set_color(YELLOW)), + run_time=1, + ) + self.wait() + self.play( + Write(language_part.copy().set_color(BLUE)), + run_time=1.5, + ) + self.wait(2)