From 2e0e5cfb5eb28e0d202c1ec7a6792948ca1e72d7 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Wed, 19 Jun 2019 16:11:46 -0700 Subject: [PATCH] Rename ode folder to diffyq --- active_projects/ode/all_part1_scenes.py | 107 - active_projects/ode/all_part2_scenes.py | 41 - active_projects/ode/all_part3_scenes.py | 70 - active_projects/ode/all_part4_scenes.py | 18 - active_projects/ode/name_animations.py | 40 - active_projects/ode/part1/pendulum.py | 1893 ----------- active_projects/ode/part1/phase_space.py | 2079 ----------- active_projects/ode/part1/pi_scenes.py | 515 --- .../ode/part1/shared_constructs.py | 118 - active_projects/ode/part1/staging.py | 3028 ----------------- active_projects/ode/part1/wordy_scenes.py | 855 ----- active_projects/ode/part2/fourier_series.py | 898 ----- active_projects/ode/part2/heat_equation.py | 2969 ---------------- active_projects/ode/part2/pi_scenes.py | 142 - .../ode/part2/shared_constructs.py | 35 - active_projects/ode/part2/staging.py | 794 ----- active_projects/ode/part2/wordy_scenes.py | 785 ----- active_projects/ode/part3/discrete_case.py | 303 -- .../ode/part3/pi_creature_scenes.py | 175 - active_projects/ode/part3/staging.py | 1230 ------- .../ode/part3/temperature_graphs.py | 2830 --------------- active_projects/ode/part3/wordy_scenes.py | 919 ----- .../ode/part4/fourier_series_scenes.py | 242 -- .../ode/part4/pi_creature_scenes.py | 22 - active_projects/ode/part4/staging.py | 345 -- .../ode/solve_pendulum_ode_sample_code.py | 63 - 26 files changed, 20516 deletions(-) delete mode 100644 active_projects/ode/all_part1_scenes.py delete mode 100644 active_projects/ode/all_part2_scenes.py delete mode 100644 active_projects/ode/all_part3_scenes.py delete mode 100644 active_projects/ode/all_part4_scenes.py delete mode 100644 active_projects/ode/name_animations.py delete mode 100644 active_projects/ode/part1/pendulum.py delete mode 100644 active_projects/ode/part1/phase_space.py delete mode 100644 active_projects/ode/part1/pi_scenes.py delete mode 100644 active_projects/ode/part1/shared_constructs.py delete mode 100644 active_projects/ode/part1/staging.py delete mode 100644 active_projects/ode/part1/wordy_scenes.py delete mode 100644 active_projects/ode/part2/fourier_series.py delete mode 100644 active_projects/ode/part2/heat_equation.py delete mode 100644 active_projects/ode/part2/pi_scenes.py delete mode 100644 active_projects/ode/part2/shared_constructs.py delete mode 100644 active_projects/ode/part2/staging.py delete mode 100644 active_projects/ode/part2/wordy_scenes.py delete mode 100644 active_projects/ode/part3/discrete_case.py delete mode 100644 active_projects/ode/part3/pi_creature_scenes.py delete mode 100644 active_projects/ode/part3/staging.py delete mode 100644 active_projects/ode/part3/temperature_graphs.py delete mode 100644 active_projects/ode/part3/wordy_scenes.py delete mode 100644 active_projects/ode/part4/fourier_series_scenes.py delete mode 100644 active_projects/ode/part4/pi_creature_scenes.py delete mode 100644 active_projects/ode/part4/staging.py delete 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 deleted file mode 100644 index 0ca648d4..00000000 --- a/active_projects/ode/all_part1_scenes.py +++ /dev/null @@ -1,107 +0,0 @@ -from active_projects.ode.part1.pendulum import * -from active_projects.ode.part1.staging import * -from active_projects.ode.part1.pi_scenes import * -from active_projects.ode.part1.phase_space import * -from active_projects.ode.part1.wordy_scenes import * - -OUTPUT_DIRECTORY = "ode/part1" -SCENES_IN_ORDER = [ - WhenChangeIsEasier, - VectorFieldTest, - IntroducePendulum, - MultiplePendulumsOverlayed, - PeriodFormula, - FormulasAreLies, - MediumAnglePendulum, - MediumHighAnglePendulum, - HighAnglePendulum, - LowAnglePendulum, - SomeOfYouWatching, - SmallAngleApproximationTex, - 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, - # - 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, - TweakMuInFormula, - TweakMuInVectorField, - FromODEToVectorField, - LorenzVectorField, - ThreeBodiesInSpace, - AltThreeBodiesInSpace, - TwoBodiesInSpace, - TwoBodiesWithZPart, - ThreeBodyTitle, - ThreeBodySymbols, - # - HighAmplitudePendulum, - WritePhaseSpace, - # - AskAboutActuallySolving, - WriteODESolvingCode, - TakeManyTinySteps, - ManyStepsFromDifferentStartingPoints, - InaccurateComputation, - HungerForExactness, - ShowRect, - ShowSquare, - JumpToThisPoint, - ThreeBodyEquation, - ItGetsWorse, - ChaosTitle, - RevisitQuote, - EndScreen, - Thumbnail, -] diff --git a/active_projects/ode/all_part2_scenes.py b/active_projects/ode/all_part2_scenes.py deleted file mode 100644 index c3883973..00000000 --- a/active_projects/ode/all_part2_scenes.py +++ /dev/null @@ -1,41 +0,0 @@ -from active_projects.ode.part2.staging import * -from active_projects.ode.part2.fourier_series import * -from active_projects.ode.part2.heat_equation import * -from active_projects.ode.part2.pi_scenes import * -from active_projects.ode.part2.wordy_scenes import * - -OUTPUT_DIRECTORY = "ode/part2" -SCENES_IN_ORDER = [ - PartTwoOfTour, - HeatEquationIntroTitle, - BrownianMotion, - BlackScholes, - ContrastChapters1And2, - FourierSeriesIntro, - FourierSeriesIntroBackground20, - ExplainCircleAnimations, - # FourierSeriesIntroBackground4, - # FourierSeriesIntroBackground8, - # FourierSeriesIntroBackground12, - TwoDBodyWithManyTemperatures, - TwoDBodyWithManyTemperaturesGraph, - TwoDBodyWithManyTemperaturesContour, - BringTwoRodsTogether, - ShowEvolvingTempGraphWithArrows, - # TodaysTargetWrapper, - WriteHeatEquation, - ReactionsToInitialHeatEquation, - TalkThrough1DHeatGraph, - ShowCubeFormation, - CompareInputsOfGeneralCaseTo1D, - ContrastXChangesToTChanges, - ShowPartialDerivativeSymbols, - WriteHeatEquation, - ShowCurvatureToRateOfChangeIntuition, - ContrastPDEToODE, - TransitionToTempVsTime, - Show1DAnd3DEquations, - # - AskAboutWhereEquationComesFrom, - DiscreteSetup, -] diff --git a/active_projects/ode/all_part3_scenes.py b/active_projects/ode/all_part3_scenes.py deleted file mode 100644 index 6fe24953..00000000 --- a/active_projects/ode/all_part3_scenes.py +++ /dev/null @@ -1,70 +0,0 @@ -from active_projects.ode.part3.staging import * -from active_projects.ode.part3.temperature_graphs import * -from active_projects.ode.part3.pi_creature_scenes import * -from active_projects.ode.part3.wordy_scenes import * -from active_projects.ode.part3.discrete_case import * - - -OUTPUT_DIRECTORY = "ode/part3" -SCENES_IN_ORDER = [ - LastChapterWrapper, - ThreeConstraints, - OceanOfPossibilities, - ThreeMainObservations, - SimpleCosExpGraph, - AddMultipleSolutions, - FourierSeriesIllustraiton, - BreakDownAFunction, - SineCurveIsUnrealistic, - AnalyzeSineCurve, - EquationAboveSineAnalysis, - ExponentialDecay, - InvestmentGrowth, - GrowingPileOfMoney, - CarbonDecayCurve, - CarbonDecayingInMammoth, - SineWaveScaledByExp, - ShowSinExpDerivatives, - IfOnly, - BoundaryConditionInterlude, - BoundaryConditionReference, - GiantCross, - SimulateRealSineCurve, - DerivativesOfLinearFunction, - StraightLine3DGraph, - SimulateLinearGraph, - EmphasizeBoundaryPoints, - ShowNewRuleAtDiscreteBoundary, - DiscreteEvolutionPoint25, - DiscreteEvolutionPoint1, - FlatEdgesForDiscreteEvolution, - FlatEdgesForDiscreteEvolutionTinySteps, - FlatEdgesContinuousEvolution, - FlatAtBoundaryWords, - SlopeToHeatFlow, - CloserLookAtStraightLine, - WriteOutBoundaryCondition, - SoWeGotNowhere, - ManipulateSinExpSurface, - HeatEquationFrame, - ShowFreq1CosExpDecay, - ShowFreq2CosExpDecay, - ShowFreq4CosExpDecay, - CompareFreqDecays1to2, - CompareFreqDecays1to4, - CompareFreqDecays2to4, - ShowHarmonics, - ShowHarmonicSurfaces, - - # SimpleCosExpGraph, - # AddMultipleSolutions, - # IveHeardOfThis, - # FourierSeriesOfLineIllustration, - # InFouriersShoes, -] - -PART_4_SCENES = [ - FourierSeriesIllustraiton, - FourierNameIntro, - CircleAnimationOfF, -] diff --git a/active_projects/ode/all_part4_scenes.py b/active_projects/ode/all_part4_scenes.py deleted file mode 100644 index 7e95486f..00000000 --- a/active_projects/ode/all_part4_scenes.py +++ /dev/null @@ -1,18 +0,0 @@ -from active_projects.ode.part4.staging import * -from active_projects.ode.part4.fourier_series_scenes import * -from active_projects.ode.part4.pi_creature_scenes import * - -OUTPUT_DIRECTORY = "ode/part4" -SCENES_IN_ORDER = [ - ComplexFourierSeriesExample, - ComplexFourierSeriesExampleEnd, - FourierSeriesExampleWithRectForZoom, - ZoomedInFourierSeriesExample, - RelationToOtherVideos, - WhyWouldYouCare, - # Oldies - - # FourierSeriesIllustraiton, - # FourierNameIntro, - # CircleAnimationOfF, -] diff --git a/active_projects/ode/name_animations.py b/active_projects/ode/name_animations.py deleted file mode 100644 index b96c00e9..00000000 --- a/active_projects/ode/name_animations.py +++ /dev/null @@ -1,40 +0,0 @@ -#!/usr/bin/env python -from manimlib.imports import * -from active_projects.ode.part2.fourier_series import FourierOfName - -name_color_pairs = [ - -] - -circle_counts = [ - # 10, - # 25, - 100, -] - -if __name__ == "__main__": - for name, color in name_color_pairs: - for n_circles in circle_counts: - try: - first_name = name.split(" ")[0] - scene = FourierOfName( - name_text=name, - name_color=color, - n_circles=n_circles, - file_writer_config={ - "write_to_movie": True, - "output_directory": os.path.join( - "patron_fourier_names", - first_name, - ), - "file_name": "{}_Fouierified_{}_Separate_paths".format( - first_name, - n_circles - ), - }, - camera_config={ - "frame_rate": 24, - }, - ) - except: - pass diff --git a/active_projects/ode/part1/pendulum.py b/active_projects/ode/part1/pendulum.py deleted file mode 100644 index de801579..00000000 --- a/active_projects/ode/part1/pendulum.py +++ /dev/null @@ -1,1893 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part1.shared_constructs import * - - -class Pendulum(VGroup): - CONFIG = { - "length": 3, - "gravity": 9.8, - "weight_diameter": 0.5, - "initial_theta": 0.3, - "omega": 0, - "damping": 0.1, - "top_point": 2 * UP, - "rod_style": { - "stroke_width": 3, - "stroke_color": LIGHT_GREY, - "sheen_direction": UP, - "sheen_factor": 1, - }, - "weight_style": { - "stroke_width": 0, - "fill_opacity": 1, - "fill_color": GREY_BROWN, - "sheen_direction": UL, - "sheen_factor": 0.5, - "background_stroke_color": BLACK, - "background_stroke_width": 3, - "background_stroke_opacity": 0.5, - }, - "dashed_line_config": { - "num_dashes": 25, - "stroke_color": WHITE, - "stroke_width": 2, - }, - "angle_arc_config": { - "radius": 1, - "stroke_color": WHITE, - "stroke_width": 2, - }, - "velocity_vector_config": { - "color": RED, - }, - "theta_label_height": 0.25, - "set_theta_label_height_cap": False, - "n_steps_per_frame": 100, - "include_theta_label": True, - "include_velocity_vector": False, - "velocity_vector_multiple": 0.5, - "max_velocity_vector_length_to_length_ratio": 0.5, - } - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.create_fixed_point() - self.create_rod() - self.create_weight() - self.rotating_group = VGroup(self.rod, self.weight) - self.create_dashed_line() - self.create_angle_arc() - if self.include_theta_label: - self.add_theta_label() - if self.include_velocity_vector: - self.add_velocity_vector() - - self.set_theta(self.initial_theta) - self.update() - - def create_fixed_point(self): - self.fixed_point_tracker = VectorizedPoint(self.top_point) - self.add(self.fixed_point_tracker) - return self - - def create_rod(self): - rod = self.rod = Line(UP, DOWN) - rod.set_height(self.length) - rod.set_style(**self.rod_style) - rod.move_to(self.get_fixed_point(), UP) - self.add(rod) - - def create_weight(self): - weight = self.weight = Circle() - weight.set_width(self.weight_diameter) - weight.set_style(**self.weight_style) - weight.move_to(self.rod.get_end()) - self.add(weight) - - def create_dashed_line(self): - line = self.dashed_line = DashedLine( - self.get_fixed_point(), - self.get_fixed_point() + self.length * DOWN, - **self.dashed_line_config - ) - line.add_updater( - lambda l: l.move_to(self.get_fixed_point(), UP) - ) - self.add_to_back(line) - - def create_angle_arc(self): - self.angle_arc = always_redraw(lambda: Arc( - arc_center=self.get_fixed_point(), - start_angle=-90 * DEGREES, - angle=self.get_arc_angle_theta(), - **self.angle_arc_config, - )) - self.add(self.angle_arc) - - def get_arc_angle_theta(self): - # Might be changed in certain scenes - return self.get_theta() - - def add_velocity_vector(self): - def make_vector(): - omega = self.get_omega() - theta = self.get_theta() - mvlr = self.max_velocity_vector_length_to_length_ratio - max_len = mvlr * self.rod.get_length() - vvm = self.velocity_vector_multiple - multiple = np.clip( - vvm * omega, -max_len, max_len - ) - vector = Vector( - multiple * RIGHT, - **self.velocity_vector_config, - ) - vector.rotate(theta, about_point=ORIGIN) - vector.shift(self.rod.get_end()) - return vector - - self.velocity_vector = always_redraw(make_vector) - self.add(self.velocity_vector) - return self - - def add_theta_label(self): - self.theta_label = always_redraw(self.get_label) - self.add(self.theta_label) - - def get_label(self): - label = TexMobject("\\theta") - label.set_height(self.theta_label_height) - if self.set_theta_label_height_cap: - max_height = self.angle_arc.get_width() - if label.get_height() > max_height: - label.set_height(max_height) - top = self.get_fixed_point() - arc_center = self.angle_arc.point_from_proportion(0.5) - vect = arc_center - top - norm = get_norm(vect) - vect = normalize(vect) * (norm + self.theta_label_height) - label.move_to(top + vect) - return label - - # - def get_theta(self): - theta = self.rod.get_angle() - self.dashed_line.get_angle() - theta = (theta + PI) % TAU - PI - return theta - - def set_theta(self, theta): - self.rotating_group.rotate( - theta - self.get_theta() - ) - self.rotating_group.shift( - self.get_fixed_point() - self.rod.get_start(), - ) - return self - - def get_omega(self): - return self.omega - - def set_omega(self, omega): - self.omega = omega - return self - - def get_fixed_point(self): - return self.fixed_point_tracker.get_location() - - # - def start_swinging(self): - self.add_updater(Pendulum.update_by_gravity) - - def end_swinging(self): - self.remove_updater(Pendulum.update_by_gravity) - - def update_by_gravity(self, dt): - theta = self.get_theta() - omega = self.get_omega() - nspf = self.n_steps_per_frame - for x in range(nspf): - d_theta = omega * dt / nspf - d_omega = op.add( - -self.damping * omega, - -(self.gravity / self.length) * np.sin(theta), - ) * dt / nspf - theta += d_theta - omega += d_omega - self.set_theta(theta) - self.set_omega(omega) - return self - - -class GravityVector(Vector): - CONFIG = { - "color": YELLOW, - "length_multiple": 1 / 9.8, - # TODO, continually update the length based - # on the pendulum's gravity? - } - - def __init__(self, pendulum, **kwargs): - super().__init__(DOWN, **kwargs) - self.pendulum = pendulum - self.scale(self.length_multiple * pendulum.gravity) - self.attach_to_pendulum(pendulum) - - def attach_to_pendulum(self, pendulum): - self.add_updater(lambda m: m.shift( - pendulum.weight.get_center() - self.get_start(), - )) - - def add_component_lines(self): - self.component_lines = always_redraw(self.create_component_lines) - self.add(self.component_lines) - - def create_component_lines(self): - theta = self.pendulum.get_theta() - x_new = rotate(RIGHT, theta) - base = self.get_start() - tip = self.get_end() - vect = tip - base - corner = base + x_new * np.dot(vect, x_new) - kw = {"dash_length": 0.025} - return VGroup( - DashedLine(base, corner, **kw), - DashedLine(corner, tip, **kw), - ) - - -class ThetaValueDisplay(VGroup): - CONFIG = { - - } - - def __init__(self, **kwargs): - super().__init__(**kwargs) - - -class ThetaVsTAxes(Axes): - CONFIG = { - "x_min": 0, - "x_max": 8, - "y_min": -PI / 2, - "y_max": PI / 2, - "y_axis_config": { - "tick_frequency": PI / 8, - "unit_size": 1.5, - }, - "number_line_config": { - "color": "#EEEEEE", - "stroke_width": 2, - "include_tip": False, - }, - "graph_style": { - "stroke_color": GREEN, - "stroke_width": 3, - "fill_opacity": 0, - }, - } - - def __init__(self, **kwargs): - super().__init__(**kwargs) - self.add_labels() - - def add_axes(self): - self.axes = Axes(**self.axes_config) - self.add(self.axes) - - def add_labels(self): - x_axis = self.get_x_axis() - y_axis = self.get_y_axis() - - t_label = self.t_label = TexMobject("t") - t_label.next_to(x_axis.get_right(), UP, MED_SMALL_BUFF) - x_axis.label = t_label - x_axis.add(t_label) - theta_label = self.theta_label = TexMobject("\\theta(t)") - theta_label.next_to(y_axis.get_top(), UP, SMALL_BUFF) - 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)) - - def get_y_axis_coordinates(self, y_axis): - texs = [ - # "\\pi \\over 4", - # "\\pi \\over 2", - # "3 \\pi \\over 4", - # "\\pi", - "\\pi / 4", - "\\pi / 2", - "3 \\pi / 4", - "\\pi", - ] - values = np.arange(1, 5) * PI / 4 - labels = VGroup() - for pos_tex, pos_value in zip(texs, values): - neg_tex = "-" + pos_tex - neg_value = -1 * pos_value - for tex, value in (pos_tex, pos_value), (neg_tex, neg_value): - if value > self.y_max or value < self.y_min: - continue - symbol = TexMobject(tex) - symbol.scale(0.5) - point = y_axis.number_to_point(value) - symbol.next_to(point, LEFT, MED_SMALL_BUFF) - labels.add(symbol) - return labels - - def get_live_drawn_graph(self, pendulum, - t_max=None, - t_step=1.0 / 60, - **style): - style = merge_dicts_recursively(self.graph_style, style) - if t_max is None: - t_max = self.x_max - - graph = VMobject() - graph.set_style(**style) - - graph.all_coords = [(0, pendulum.get_theta())] - graph.time = 0 - graph.time_of_last_addition = 0 - - def update_graph(graph, dt): - graph.time += dt - if graph.time > t_max: - graph.remove_updater(update_graph) - return - new_coords = (graph.time, pendulum.get_theta()) - if graph.time - graph.time_of_last_addition >= t_step: - graph.all_coords.append(new_coords) - graph.time_of_last_addition = graph.time - points = [ - self.coords_to_point(*coords) - for coords in [*graph.all_coords, new_coords] - ] - graph.set_points_smoothly(points) - - graph.add_updater(update_graph) - return graph - - -# Scenes -class IntroducePendulum(PiCreatureScene, MovingCameraScene): - CONFIG = { - "pendulum_config": { - "length": 3, - "top_point": 4 * RIGHT, - "weight_diameter": 0.35, - "gravity": 20, - }, - "theta_vs_t_axes_config": { - "y_max": PI / 4, - "y_min": -PI / 4, - "y_axis_config": { - "tick_frequency": PI / 16, - "unit_size": 2, - "tip_length": 0.3, - }, - "x_max": 12, - "number_line_config": { - "stroke_width": 2, - } - }, - } - - def setup(self): - MovingCameraScene.setup(self) - PiCreatureScene.setup(self) - - def construct(self): - self.add_pendulum() - # 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() - - def create_pi_creatures(self): - randy = Randolph(color=BLUE_C) - morty = Mortimer(color=MAROON_E) - creatures = VGroup(randy, morty) - creatures.scale(0.5) - creatures.arrange(RIGHT, buff=2.5) - creatures.to_corner(DR) - return creatures - - def add_pendulum(self): - pendulum = self.pendulum = Pendulum(**self.pendulum_config) - pendulum.start_swinging() - frame = self.camera_frame - frame.save_state() - frame.scale(0.5) - frame.move_to(pendulum.dashed_line) - - self.add(pendulum, frame) - - def label_pi_creatures(self): - randy, morty = self.pi_creatures - randy_label = TextMobject("Physics\\\\", "student") - morty_label = TextMobject("Physics\\\\", "teacher") - labels = VGroup(randy_label, morty_label) - labels.scale(0.5) - randy_label.next_to(randy, UP, LARGE_BUFF) - morty_label.next_to(morty, UP, LARGE_BUFF) - - for label, pi in zip(labels, self.pi_creatures): - label.arrow = Arrow( - label.get_bottom(), pi.eyes.get_top() - ) - label.arrow.set_color(WHITE) - label.arrow.set_stroke(width=5) - - morty.labels = VGroup( - morty_label, - morty_label.arrow, - ) - - self.play( - FadeInFromDown(randy_label), - GrowArrow(randy_label.arrow), - randy.change, "hooray", - ) - self.play( - Animation(self.pendulum.fixed_point_tracker), - TransformFromCopy(randy_label[0], morty_label[0]), - FadeIn(morty_label[1]), - GrowArrow(morty_label.arrow), - morty.change, "raise_right_hand", - ) - self.wait(2) - - def label_pendulum(self): - pendulum = self.pendulum - randy, morty = self.pi_creatures - label = pendulum.theta_label - rect = SurroundingRectangle(label, buff=0.5 * SMALL_BUFF) - rect.add_updater(lambda r: r.move_to(label)) - - 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), - ) - self.wait() - - def add_graph(self): - axes = self.axes = ThetaVsTAxes(**self.theta_vs_t_axes_config) - axes.y_axis.label.next_to(axes.y_axis, UP, buff=0) - axes.to_corner(UL) - - self.play( - Restore( - self.camera_frame, - rate_func=squish_rate_func(smooth, 0, 0.9), - ), - DrawBorderThenFill( - axes, - rate_func=squish_rate_func(smooth, 0.5, 1), - lag_ratio=0.9, - ), - Transform( - self.pendulum.theta_label.copy().clear_updaters(), - axes.y_axis.label.copy(), - remover=True, - rate_func=squish_rate_func(smooth, 0, 0.8), - ), - run_time=3, - ) - - 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): - pendulum = self.pendulum - axes = self.axes - - period = self.period = TAU * np.sqrt( - pendulum.length / pendulum.gravity - ) - amplitude = pendulum.initial_theta - - line = Line( - axes.coords_to_point(0, amplitude), - axes.coords_to_point(period, amplitude), - ) - line.shift(SMALL_BUFF * RIGHT) - brace = Brace(line, UP, buff=SMALL_BUFF) - brace.add_to_back(brace.copy().set_style(BLACK, 10)) - formula = get_period_formula() - formula.next_to(brace, UP, SMALL_BUFF) - - self.period_formula = formula - self.period_brace = brace - - self.play( - GrowFromCenter(brace), - FadeInFromDown(formula), - ) - self.wait(2) - - def show_length_and_gravity(self): - formula = self.period_formula - L = formula.get_part_by_tex("L") - g = formula.get_part_by_tex("g") - - rod = self.pendulum.rod - new_rod = rod.copy() - new_rod.set_stroke(BLUE, 7) - new_rod.add_updater(lambda r: r.put_start_and_end_on( - *rod.get_start_and_end() - )) - - g_vect = GravityVector( - 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), - ShowCreation(new_rod), - ) - self.play(FadeOut(new_rod)) - - self.play( - ShowCreationThenDestructionAround(g), - GrowArrow(g_vect), - ) - self.play(self.get_down_vectors_animation(down_vectors)) - self.wait(6) - - self.gravity_vector = g_vect - - def tweak_length_and_gravity(self): - pendulum = self.pendulum - axes = self.axes - graph = self.graph - brace = self.period_brace - formula = self.period_formula - g_vect = self.gravity_vector - randy, morty = self.pi_creatures - - graph.clear_updaters() - period2 = self.period * np.sqrt(2) - period3 = self.period / np.sqrt(2) - amplitude = pendulum.initial_theta - graph2, graph3 = [ - axes.get_graph( - lambda t: amplitude * np.cos(TAU * t / p), - color=RED, - ) - for p in (period2, period3) - ] - formula.add_updater(lambda m: m.next_to( - brace, UP, SMALL_BUFF - )) - - new_pendulum_config = dict(self.pendulum_config) - new_pendulum_config["length"] *= 2 - new_pendulum_config["top_point"] += 3.5 * UP - # new_pendulum_config["initial_theta"] = pendulum.get_theta() - new_pendulum = Pendulum(**new_pendulum_config) - - down_vectors = self.get_down_vectors() - - self.play(randy.change, "happy") - self.play( - ReplacementTransform(pendulum, new_pendulum), - morty.change, "horrified", - morty.shift, 3 * RIGHT, - morty.labels.shift, 3 * RIGHT, - ) - self.remove(morty, morty.labels) - g_vect.attach_to_pendulum(new_pendulum) - new_pendulum.start_swinging() - self.play( - ReplacementTransform(graph, graph2), - brace.stretch, np.sqrt(2), 0, {"about_edge": LEFT}, - ) - self.add(g_vect) - self.wait(3) - - new_pendulum.gravity *= 4 - g_vect.scale(2) - self.play( - FadeOut(graph2), - self.get_down_vectors_animation(down_vectors) - ) - self.play( - FadeIn(graph3), - brace.stretch, 0.5, 0, {"about_edge": LEFT}, - ) - 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 = { - "initial_thetas": [ - 150 * DEGREES, - 90 * DEGREES, - 60 * DEGREES, - 30 * DEGREES, - 10 * DEGREES, - ], - "weight_colors": [ - PINK, RED, GREEN, BLUE, GREY, - ], - "pendulum_config": { - "top_point": ORIGIN, - "length": 3, - }, - } - - def construct(self): - pendulums = VGroup(*[ - Pendulum( - initial_theta=theta, - weight_style={ - "fill_color": wc, - "fill_opacity": 0.5, - }, - **self.pendulum_config, - ) - for theta, wc in zip( - self.initial_thetas, - self.weight_colors, - ) - ]) - for pendulum in pendulums: - pendulum.start_swinging() - pendulum.remove(pendulum.theta_label) - - randy = Randolph(color=BLUE_C) - randy.to_corner(DL) - randy.add_updater(lambda r: r.look_at(pendulums[0].weight)) - - axes = ThetaVsTAxes( - x_max=20, - y_axis_config={ - "unit_size": 0.5, - "tip_length": 0.3, - }, - ) - axes.to_corner(UL) - graphs = VGroup(*[ - axes.get_live_drawn_graph( - pendulum, - stroke_color=pendulum.weight.get_color(), - stroke_width=1, - ) - for pendulum in pendulums - ]) - - self.add(pendulums) - self.add(axes, *graphs) - self.play(randy.change, "sassy") - self.wait(2) - self.play(Blink(randy)) - self.wait(5) - self.play(randy.change, "angry") - self.play(Blink(randy)) - self.wait(10) - - -class LowAnglePendulum(Scene): - CONFIG = { - "pendulum_config": { - "initial_theta": 20 * DEGREES, - "length": 2.0, - "damping": 0, - "top_point": ORIGIN, - }, - "axes_config": { - "y_axis_config": {"unit_size": 0.75}, - "x_axis_config": { - "unit_size": 0.5, - "numbers_to_show": range(2, 25, 2), - "number_scale_val": 0.5, - }, - "x_max": 25, - "number_line_config": { - "tip_length": 0.3, - "stroke_width": 2, - } - }, - "axes_corner": UL, - } - - def construct(self): - pendulum = Pendulum(**self.pendulum_config) - axes = ThetaVsTAxes(**self.axes_config) - axes.center() - axes.to_corner(self.axes_corner, buff=LARGE_BUFF) - graph = axes.get_live_drawn_graph(pendulum) - - L = pendulum.length - g = pendulum.gravity - theta0 = pendulum.initial_theta - prediction = axes.get_graph( - lambda t: theta0 * np.cos(t * np.sqrt(g / L)) - ) - dashed_prediction = DashedVMobject(prediction, num_dashes=300) - dashed_prediction.set_stroke(WHITE, 1) - prediction_formula = TexMobject( - "\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)" - ) - prediction_formula.scale(0.75) - prediction_formula.next_to( - dashed_prediction, UP, SMALL_BUFF, - ) - - theta0 = prediction_formula.get_part_by_tex("\\theta_0") - theta0_brace = Brace(theta0, UP, buff=SMALL_BUFF) - theta0_brace.stretch(0.5, 1, about_edge=DOWN) - theta0_label = Integer( - pendulum.initial_theta * 180 / PI, - unit="^\\circ" - ) - theta0_label.scale(0.75) - theta0_label.next_to(theta0_brace, UP, SMALL_BUFF) - - group = VGroup(theta0_brace, theta0_label, prediction_formula) - group.shift_onto_screen(buff=MED_SMALL_BUFF) - - self.add(axes, dashed_prediction, pendulum) - self.play( - ShowCreation(dashed_prediction, run_time=2), - FadeInFromDown(prediction_formula), - FadeInFromDown(theta0_brace), - FadeInFromDown(theta0_label), - ) - self.play( - ShowCreationThenFadeAround(theta0_label), - ShowCreationThenFadeAround(pendulum.theta_label), - ) - self.wait() - - pendulum.start_swinging() - self.add(graph) - self.wait(30) - - -class ApproxWordsLowAnglePendulum(Scene): - def construct(self): - period = TexMobject( - "\\text{Period}", "\\approx", - "2\\pi \\sqrt{\\,{L} / {g}}", - **Lg_formula_config - ) - checkmark = TexMobject("\\checkmark") - checkmark.set_color(GREEN) - checkmark.scale(2) - checkmark.next_to(period, RIGHT, MED_LARGE_BUFF) - - self.add(period, checkmark) - - -class MediumAnglePendulum(LowAnglePendulum): - CONFIG = { - "pendulum_config": { - "initial_theta": 50 * DEGREES, - "n_steps_per_frame": 1000, - }, - "axes_config": { - "y_axis_config": {"unit_size": 0.75}, - "y_max": PI / 2, - "y_min": -PI / 2, - "number_line_config": { - "tip_length": 0.3, - "stroke_width": 2, - } - }, - "pendulum_shift_vect": 1 * RIGHT, - } - - -class MediumHighAnglePendulum(MediumAnglePendulum): - CONFIG = { - "pendulum_config": { - "initial_theta": 90 * DEGREES, - "n_steps_per_frame": 1000, - }, - } - - -class HighAnglePendulum(LowAnglePendulum): - CONFIG = { - "pendulum_config": { - "initial_theta": 175 * DEGREES, - "n_steps_per_frame": 1000, - "top_point": 1.5 * DOWN, - "length": 2, - }, - "axes_config": { - "y_axis_config": {"unit_size": 0.5}, - "y_max": PI, - "y_min": -PI, - "number_line_config": { - "tip_length": 0.3, - "stroke_width": 2, - } - }, - "pendulum_shift_vect": 1 * RIGHT, - } - - -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 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": { - "length": 5, - "top_point": 3.5 * UP, - "initial_theta": 60 * DEGREES, - "set_theta_label_height_cap": True, - }, - "g_vect_config": { - "length_multiple": 0.25, - }, - "tan_line_color": BLUE, - "perp_line_color": PINK, - } - - 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_angle_geometry() - self.show_gsin_formula() - self.show_sign() - self.show_acceleration_formula() - # self.ask_about_what_to_do() - - # 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) - theta_tracker = ValueTracker(pendulum.get_theta()) - pendulum.add_updater(lambda p: p.set_theta( - theta_tracker.get_value() - )) - - self.add(pendulum) - 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 - - g_vect = self.g_vect = GravityVector( - pendulum, **self.g_vect_config, - ) - g_word = self.g_word = TextMobject("Gravity") - g_word.rotate(-90 * DEGREES) - g_word.scale(0.75) - g_word.add_updater(lambda m: m.next_to( - g_vect, RIGHT, buff=-SMALL_BUFF, - )) - - self.play( - GrowArrow(g_vect), - FadeInFrom(g_word, UP, lag_ratio=0.1), - ) - self.wait() - - def show_constraint(self): - pendulum = self.pendulum - - arcs = VGroup() - for u in [-1, 2, -1]: - d_theta = 40 * DEGREES * u - arc = Arc( - start_angle=pendulum.get_theta() - 90 * DEGREES, - angle=d_theta, - radius=pendulum.length, - arc_center=pendulum.get_fixed_point(), - stroke_width=2, - stroke_color=YELLOW, - stroke_opacity=0.5, - ) - self.play( - self.theta_tracker.increment_value, d_theta, - ShowCreation(arc) - ) - arcs.add(arc) - self.play(FadeOut(arcs)) - - def break_g_vect_into_components(self): - g_vect = self.g_vect - g_vect.component_lines = always_redraw( - g_vect.create_component_lines - ) - tan_line, perp_line = g_vect.component_lines - g_vect.tangent = always_redraw(lambda: Arrow( - tan_line.get_start(), - tan_line.get_end(), - buff=0, - color=self.tan_line_color, - )) - g_vect.perp = always_redraw(lambda: Arrow( - perp_line.get_start(), - perp_line.get_end(), - buff=0, - color=self.perp_line_color, - )) - - self.play(ShowCreation(g_vect.component_lines)) - self.play(GrowArrow(g_vect.tangent)) - self.wait() - 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 - g_word.clear_updaters() - - g_term = self.g_term = TexMobject("-g") - g_term.add_updater(lambda m: m.next_to( - g_vect, - RIGHT if self.pendulum.get_theta() >= 0 else LEFT, - SMALL_BUFF - )) - - def create_vect_label(vect, tex, direction): - label = TexMobject(tex) - label.set_stroke(width=0, background=True) - label.add_background_rectangle() - label.scale(0.7) - max_width = 0.9 * vect.get_length() - if label.get_width() > max_width: - label.set_width(max_width) - angle = vect.get_angle() - angle = (angle + PI / 2) % PI - PI / 2 - label.next_to(ORIGIN, direction, SMALL_BUFF) - label.rotate(angle, about_point=ORIGIN) - label.shift(vect.get_center()) - return label - - g_sin_label = always_redraw(lambda: create_vect_label( - g_vect.tangent, "-g\\sin(\\theta)", UP, - )) - g_cos_label = always_redraw(lambda: create_vect_label( - g_vect.perp, "-g\\cos(\\theta)", DOWN, - )) - - self.play( - ReplacementTransform(g_word[0][0], g_term[0][1]), - FadeOut(g_word[0][1:]), - Write(g_term[0][0]), - ) - self.add(g_term) - self.wait() - for label in g_sin_label, g_cos_label: - self.play( - GrowFromPoint(label[0], g_term.get_center()), - TransformFromCopy(g_term, label[1][:2]), - GrowFromPoint(label[1][2:], g_term.get_center()), - remover=True - ) - self.add(label) - self.wait() - - self.g_sin_label = g_sin_label - self.g_cos_label = g_cos_label - - 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( - get_theta() - )) - theta_decimal.add_updater(lambda m: m.next_to( - self.pendulum.theta_label, DOWN - )) - theta_decimal.add_updater(lambda m: m.set_color( - 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, - FadeIn(self.x_eq), - run_time=2, - ) - self.wait() - - def ask_about_what_to_do(self): - g_vect = self.g_vect - g_sin_label = self.g_sin_label - angle = g_vect.tangent.get_angle() - angle = (angle - PI) % TAU - - randy = You() - randy.to_corner(DL) - bubble = randy.get_bubble( - height=2, - width=3.5, - ) - g_sin_copy = g_sin_label.copy() - g_sin_copy.remove(g_sin_copy[0]) - g_sin_copy.generate_target() - g_sin_copy.target.scale(1 / 0.75) - g_sin_copy.target.rotate(-angle) - a_eq = TexMobject("a=") - thought_term = VGroup(a_eq, g_sin_copy.target) - thought_term.arrange(RIGHT, buff=SMALL_BUFF) - thought_term.move_to(bubble.get_bubble_center()) - - rect = SurroundingRectangle(g_sin_copy.target) - rect.rotate(angle) - rect.move_to(g_sin_label) - - randy.save_state() - randy.fade(1) - self.play(randy.restore, randy.change, "pondering") - self.play(ShowCreationThenFadeOut(rect)) - self.play( - ShowCreation(bubble), - Write(a_eq), - MoveToTarget(g_sin_copy), - randy.look_at, bubble, - ) - thought_term.remove(g_sin_copy.target) - thought_term.add(g_sin_copy) - self.play(Blink(randy)) - self.wait() - self.play( - ShowCreationThenDestruction( - thought_term.copy().set_style( - stroke_color=YELLOW, - stroke_width=2, - fill_opacity=0, - ), - run_time=2, - lag_ratio=0.2, - ), - randy.change, "confused", thought_term, - ) - self.play(Blink(randy)) - self.play( - FadeOut(randy), - FadeOut(bubble), - thought_term.next_to, self.pendulum, DOWN, LARGE_BUFF - ) - - self.accleration_equation = thought_term - - def emphasize_theta(self): - pendulum = self.pendulum - - self.play(FocusOn(pendulum.theta_label)) - self.play(Indicate(pendulum.theta_label)) - - 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_copy.end_swinging() - self.remove(pendulum_copy) - pendulum.remove_updater(new_updater) - self.update_mobjects(0) - - def show_angular_velocity(self): - pass - - def show_angular_acceleration(self): - pass - - def circle_g_sin_formula(self): - self.play( - ShowCreationThenFadeAround( - self.accleration_equation - ) - ) - - # - def set_theta(self, value, *added_anims, **kwargs): - kwargs["run_time"] = kwargs.get("run_time", 2) - self.play( - self.theta_tracker.set_value, value, - *added_anims, - **kwargs, - ) - - -class BuildUpEquation(Scene): - CONFIG = { - "tex_config": { - "tex_to_color_map": { - "{a}": YELLOW, - "{v}": RED, - "{x}": GREEN, - "\\theta": BLUE, - "{L}": WHITE, - } - } - } - - def construct(self): - # self.add_center_line() - self.show_derivatives() - self.show_theta_double_dot_equation() - self.talk_about_sine_component() - self.add_air_resistance() - - def add_center_line(self): - line = Line(UP, DOWN) - line.set_height(FRAME_HEIGHT) - line.set_stroke(WHITE, 1) - self.add(line) - - def show_derivatives(self): - a_eq = TexMobject( - "{a}", "=", "{d{v} \\over dt}", - **self.tex_config, - ) - v_eq = TexMobject( - "{v}", "=", "{d{x} \\over dt}", - **self.tex_config, - ) - x_eq = TexMobject( - "{x} = {L} \\theta", - **self.tex_config, - ) - eqs = VGroup(a_eq, v_eq, x_eq) - eqs.arrange(DOWN, buff=LARGE_BUFF) - eqs.to_corner(UL) - - v_rhs = TexMobject( - "={L}{d\\theta \\over dt}", - "=", "{L}\\dot{\\theta}", - **self.tex_config, - ) - - v_rhs.next_to(v_eq, RIGHT, SMALL_BUFF) - v_rhs.shift( - UP * (v_eq[1].get_bottom()[1] - v_rhs[0].get_bottom()[1]) - ) - a_rhs = TexMobject( - "={L}{d", "\\dot{\\theta}", "\\over dt}", - "=", "{L}\\ddot{\\theta}", - **self.tex_config, - ) - a_rhs.next_to(a_eq, RIGHT, SMALL_BUFF) - a_rhs.shift( - UP * (a_eq[1].get_bottom()[1] - a_rhs[0].get_bottom()[1]) - ) - - # a_eq - self.play(Write(a_eq)) - self.wait() - - # v_eq - self.play( - TransformFromCopy( - a_eq.get_part_by_tex("{v}"), - v_eq.get_part_by_tex("{v}"), - ) - ) - self.play(TransformFromCopy(v_eq[:1], v_eq[1:])) - self.wait() - - # x_eq - self.play( - TransformFromCopy( - v_eq.get_part_by_tex("{x}"), - x_eq.get_part_by_tex("{x}"), - ) - ) - self.play(Write(x_eq[1:])) - self.wait() - for tex in "L", "\\theta": - self.play(ShowCreationThenFadeAround( - x_eq.get_part_by_tex(tex) - )) - self.wait() - - # v_rhs - self.play(*[ - TransformFromCopy( - x_eq.get_part_by_tex(tex), - v_rhs.get_part_by_tex(tex), - ) - for tex in ("=", "{L}", "\\theta") - ]) - self.play( - TransformFromCopy(v_eq[-3], v_rhs[2]), - TransformFromCopy(v_eq[-1], v_rhs[4]), - ) - self.wait() - self.play( - Write(v_rhs[-5]), - TransformFromCopy(*v_rhs.get_parts_by_tex("{L}")), - TransformFromCopy(v_rhs[3:4], v_rhs[-3:]) - ) - self.wait() - self.play(ShowCreationThenFadeAround(v_rhs[2:4])) - self.play(ShowCreationThenFadeAround(v_rhs[4])) - self.wait() - - # a_rhs - self.play(*[ - TransformFromCopy( - v_rhs.get_parts_by_tex(tex)[-1], - a_rhs.get_part_by_tex(tex), - ) - for tex in ("=", "{L}", "\\theta", "\\dot") - ]) - self.play( - TransformFromCopy(a_eq[-3], a_rhs[2]), - TransformFromCopy(a_eq[-1], a_rhs[6]), - ) - self.wait() - self.play( - Write(a_rhs[-5]), - TransformFromCopy(*a_rhs.get_parts_by_tex("{L}")), - TransformFromCopy(a_rhs[3:4], a_rhs[-3:]), - ) - self.wait() - - self.equations = VGroup( - a_eq, v_eq, x_eq, - v_rhs, a_rhs, - ) - - def show_theta_double_dot_equation(self): - equations = self.equations - a_deriv = equations[0] - a_rhs = equations[-1][-5:].copy() - - shift_vect = 1.5 * DOWN - - equals = TexMobject("=") - equals.rotate(90 * DEGREES) - equals.next_to(a_deriv[0], UP, MED_LARGE_BUFF) - g_sin_eq = TexMobject( - "-", "g", "\\sin", "(", "\\theta", ")", - **self.tex_config, - ) - g_sin_eq.next_to( - equals, UP, - buff=MED_LARGE_BUFF, - aligned_edge=LEFT, - ) - g_sin_eq.to_edge(LEFT) - g_sin_eq.shift(shift_vect) - - shift_vect += ( - g_sin_eq[1].get_center() - - a_deriv[0].get_center() - )[0] * RIGHT - - equals.shift(shift_vect) - a_rhs.shift(shift_vect) - - self.play( - equations.shift, shift_vect, - Write(equals), - GrowFromPoint( - g_sin_eq, 2 * RIGHT + 3 * DOWN - ) - ) - self.wait() - self.play( - a_rhs.next_to, g_sin_eq, RIGHT, - a_rhs.shift, SMALL_BUFF * UP, - ) - self.wait() - - # Fade equations - self.play( - FadeOut(equals), - equations.shift, DOWN, - equations.fade, 0.5, - ) - - # Rotate sides - equals, L, ddot, theta, junk = a_rhs - L_dd_theta = VGroup(L, ddot, theta) - minus, g, sin, lp, theta2, rp = g_sin_eq - m2, g2, over, L2 = frac = TexMobject("-", "{g", "\\over", "L}") - frac.next_to(equals, RIGHT) - - self.play( - L_dd_theta.next_to, equals, LEFT, - L_dd_theta.shift, SMALL_BUFF * UP, - g_sin_eq.next_to, equals, RIGHT, - path_arc=PI / 2, - ) - self.play( - ReplacementTransform(g, g2), - ReplacementTransform(minus, m2), - ReplacementTransform(L, L2), - Write(over), - g_sin_eq[2:].next_to, over, RIGHT, SMALL_BUFF, - ) - self.wait() - - # Surround - rect = SurroundingRectangle(VGroup(g_sin_eq, frac, ddot)) - rect.stretch(1.1, 0) - dashed_rect = DashedVMobject( - rect, num_dashes=50, positive_space_ratio=1, - ) - dashed_rect.shuffle() - dashed_rect.save_state() - dashed_rect.space_out_submobjects(1.1) - for piece in dashed_rect: - piece.rotate(90 * DEGREES) - dashed_rect.fade(1) - self.play(Restore(dashed_rect, lag_ratio=0.05)) - dashed_rect.generate_target() - dashed_rect.target.space_out_submobjects(0.9) - dashed_rect.target.fade(1) - for piece in dashed_rect.target: - piece.rotate(90 * DEGREES) - self.play(MoveToTarget( - dashed_rect, - lag_ratio=0.05, - remover=True - )) - self.wait() - - self.main_equation = VGroup( - ddot, theta, equals, - m2, L2, over, g2, - sin, lp, theta2, rp, - ) - - def talk_about_sine_component(self): - main_equation = self.main_equation - gL_part = main_equation[4:7] - sin_part = main_equation[7:] - sin = sin_part[0] - - morty = Mortimer(height=1.5) - morty.next_to(sin, DR, buff=LARGE_BUFF) - morty.add_updater(lambda m: m.look_at(sin)) - - self.play(ShowCreationThenFadeAround(gL_part)) - self.wait() - self.play(ShowCreationThenFadeAround(sin_part)) - self.wait() - - self.play(FadeIn(morty)) - sin.save_state() - self.play( - morty.change, "angry", - sin.next_to, morty, LEFT, {"aligned_edge": UP}, - ) - self.play(Blink(morty)) - morty.clear_updaters() - self.play( - morty.change, "concerned_musician", - morty.look, DR, - ) - self.play(Restore(sin)) - self.play(FadeOut(morty)) - self.wait() - - # Emphasize theta as input - theta = sin_part[2] - arrow = Vector(0.5 * UP, color=WHITE) - arrow.next_to(theta, DOWN, SMALL_BUFF) - word = TextMobject("Input") - word.next_to(arrow, DOWN) - - self.play( - FadeInFrom(word, UP), - GrowArrow(arrow) - ) - self.play( - ShowCreationThenDestruction( - theta.copy().set_style( - fill_opacity=0, - stroke_width=2, - stroke_color=YELLOW, - ), - lag_ratio=0.1, - ) - ) - self.play(FadeOut(arrow), FadeOut(word)) - - def add_air_resistance(self): - main_equation = self.main_equation - tdd_eq = main_equation[:3] - rhs = main_equation[3:] - - new_term = TexMobject( - "-", "\\mu", "\\dot{", "\\theta}", - ) - new_term.set_color_by_tex("\\theta", BLUE) - new_term.move_to(main_equation) - new_term.shift(0.5 * SMALL_BUFF * UP) - new_term[0].align_to(rhs[0], UP) - - brace = Brace(new_term, DOWN) - words = brace.get_text("Air resistance") - - self.play( - FadeInFromDown(new_term), - tdd_eq.next_to, new_term, LEFT, - tdd_eq.align_to, tdd_eq, UP, - rhs.next_to, new_term, RIGHT, - rhs.align_to, rhs, UP, - ) - self.play( - GrowFromCenter(brace), - Write(words) - ) - 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 deleted file mode 100644 index 549ac28b..00000000 --- a/active_projects/ode/part1/phase_space.py +++ /dev/null @@ -1,2079 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part1.shared_constructs import * -from active_projects.ode.part1.pendulum import Pendulum - - -# TODO: Arguably separate the part showing many -# configurations with the part showing just one. -class VisualizeStates(Scene): - CONFIG = { - "coordinate_plane_config": { - "y_line_frequency": PI / 2, - # "x_line_frequency": PI / 2, - "x_line_frequency": 1, - "y_axis_config": { - # "unit_size": 1.75, - "unit_size": 1, - }, - "y_max": 4, - "faded_line_ratio": 4, - "background_line_style": { - "stroke_width": 1, - }, - }, - "little_pendulum_config": { - "length": 1, - "gravity": 4.9, - "weight_diameter": 0.3, - "include_theta_label": False, - "include_velocity_vector": True, - "angle_arc_config": { - "radius": 0.2, - }, - "velocity_vector_config": { - "max_tip_length_to_length_ratio": 0.35, - "max_stroke_width_to_length_ratio": 6, - }, - "velocity_vector_multiple": 0.25, - "max_velocity_vector_length_to_length_ratio": 0.8, - }, - "big_pendulum_config": { - "length": 1.6, - "gravity": 4.9, - "damping": 0.2, - "weight_diameter": 0.3, - "include_velocity_vector": True, - "angle_arc_config": { - "radius": 0.5, - }, - "initial_theta": 80 * DEGREES, - "omega": -1, - "set_theta_label_height_cap": True, - }, - "n_thetas": 11, - "n_omegas": 7, - # "n_thetas": 5, - # "n_omegas": 3, - "initial_grid_wait_time": 15, - } - - def construct(self): - self.initialize_plane() - - simple = False - if simple: - self.add(self.plane) - else: - self.initialize_grid_of_states() - self.show_all_states_evolving() - self.show_grid_of_states_creation() - self.collapse_grid_into_points() - self.show_state_with_pair_of_numbers() - self.show_acceleration_dependence() - self.show_evolution_from_a_start_state() - - def initialize_grid_of_states(self): - pendulums = VGroup() - rects = VGroup() - state_grid = VGroup() - thetas = self.get_initial_thetas() - omegas = self.get_initial_omegas() - for omega in omegas: - row = VGroup() - for theta in thetas: - rect = Rectangle( - height=3, - width=3, - stroke_color=WHITE, - stroke_width=2, - fill_color=DARKER_GREY, - fill_opacity=1, - ) - pendulum = Pendulum( - initial_theta=theta, - omega=omega, - top_point=rect.get_center(), - **self.little_pendulum_config, - ) - pendulum.add_velocity_vector() - pendulums.add(pendulum) - rects.add(rect) - state = VGroup(rect, pendulum) - state.rect = rect - state.pendulum = pendulum - row.add(state) - row.arrange(RIGHT, buff=0) - state_grid.add(row) - state_grid.arrange(UP, buff=0) - - state_grid.set_height(FRAME_HEIGHT) - state_grid.center() - state_grid.save_state(use_deepcopy=True) - - self.state_grid = state_grid - self.pendulums = pendulums - - def initialize_plane(self): - plane = self.plane = NumberPlane( - **self.coordinate_plane_config - ) - plane.axis_labels = VGroup( - plane.get_x_axis_label( - "\\theta", RIGHT, UL, buff=SMALL_BUFF - ), - plane.get_y_axis_label( - "\\dot \\theta", UP, DR, buff=SMALL_BUFF - ).set_color(YELLOW), - ) - for label in plane.axis_labels: - label.add_background_rectangle() - plane.add(plane.axis_labels) - - plane.y_axis.add_numbers(direction=DL) - - x_axis = plane.x_axis - label_texs = ["\\pi \\over 2", "\\pi", "3\\pi \\over 2", "\\tau"] - values = [PI / 2, PI, 3 * PI / 2, TAU] - x_axis.coordinate_labels = VGroup() - x_axis.add(x_axis.coordinate_labels) - for value, label_tex in zip(values, label_texs): - for u in [-1, 1]: - tex = label_tex - if u < 0: - tex = "-" + tex - label = TexMobject(tex) - label.scale(0.5) - if label.get_height() > 0.4: - label.set_height(0.4) - point = x_axis.number_to_point(u * value) - label.next_to(point, DR, SMALL_BUFF) - x_axis.coordinate_labels.add(label) - return plane - - def show_all_states_evolving(self): - state_grid = self.state_grid - pendulums = self.pendulums - - for pendulum in pendulums: - pendulum.start_swinging() - - self.add(state_grid) - self.wait(self.initial_grid_wait_time) - - def show_grid_of_states_creation(self): - self.remove(self.state_grid) - self.initialize_grid_of_states() # Again - state_grid = self.state_grid - - title = TextMobject("All states") - title.to_edge(UP, buff=MED_SMALL_BUFF) - self.all_states_title = title - - state_grid.set_height( - FRAME_HEIGHT - title.get_height() - 2 * MED_SMALL_BUFF - ) - state_grid.to_edge(DOWN, buff=0) - - def place_at_top(state): - state.set_height(3) - state.center() - state.next_to(title, DOWN) - - middle_row = state_grid[len(state_grid) // 2] - middle_row_copy = middle_row.deepcopy() - right_column_copy = VGroup(*[ - row[-1] for row in state_grid - ]).deepcopy() - - for state in it.chain(middle_row_copy, right_column_copy): - place_at_top(state) - - self.add(title) - self.play( - ShowIncreasingSubsets(middle_row), - ShowIncreasingSubsets(middle_row_copy), - run_time=2, - rate_func=linear, - ) - self.wait() - self.play( - ShowIncreasingSubsets(state_grid), - ShowIncreasingSubsets(right_column_copy), - run_time=2, - rate_func=linear, - ) - self.remove(middle_row_copy) - self.remove(middle_row) - self.add(state_grid) - self.remove(right_column_copy) - self.play( - ReplacementTransform( - right_column_copy[-1], - state_grid[-1][-1], - remover=True - ) - ) - self.wait() - - def collapse_grid_into_points(self): - state_grid = self.state_grid - plane = self.plane - - dots = VGroup() - for row in state_grid: - for state in row: - dot = Dot( - self.get_state_point(state.pendulum), - radius=0.05, - color=YELLOW, - background_stroke_width=3, - background_stroke_color=BLACK, - ) - dot.state = state - dots.add(dot) - - self.add(plane) - self.remove(state_grid) - flat_state_group = VGroup(*it.chain(*state_grid)) - flat_dot_group = VGroup(*it.chain(*dots)) - self.clear() # The nuclear option - self.play( - ShowCreation(plane), - FadeOut(self.all_states_title), - LaggedStart(*[ - TransformFromCopy(m1, m2) - for m1, m2 in zip(flat_state_group, flat_dot_group) - ], lag_ratio=0.1, run_time=4) - ) - self.clear() # Again, not sure why I need this - self.add(plane, dots) - self.wait() - - self.state_dots = dots - - def show_state_with_pair_of_numbers(self): - axis_labels = self.plane.axis_labels - - state = self.get_flexible_state_picture() - dot = self.get_state_controlling_dot(state) - h_line = always_redraw( - lambda: self.get_tracking_h_line(dot.get_center()) - ) - v_line = always_redraw( - lambda: self.get_tracking_v_line(dot.get_center()) - ) - - self.add(dot) - anims = [GrowFromPoint(state, dot.get_center())] - if hasattr(self, "state_dots"): - anims.append(FadeOut(self.state_dots)) - self.play(*anims) - - for line, label in zip([h_line, v_line], axis_labels): - # self.add(line, dot) - self.play( - ShowCreation(line), - ShowCreationThenFadeAround(label), - run_time=1 - ) - for vect in LEFT, 3 * UP: - self.play( - ApplyMethod( - dot.shift, vect, - rate_func=there_and_back, - run_time=2, - ) - ) - self.wait() - for vect in 2 * LEFT, 3 * UP, 2 * RIGHT, 2 * DOWN: - self.play(dot.shift, vect, run_time=1.5) - self.wait() - - self.state = state - self.state_dot = dot - self.h_line = h_line - self.v_line = v_line - - def show_acceleration_dependence(self): - ode = get_ode() - thetas = ode.get_parts_by_tex("\\theta") - thetas[0].set_color(RED) - thetas[1].set_color(YELLOW) - ode.move_to( - FRAME_WIDTH * RIGHT / 4 + - FRAME_HEIGHT * UP / 4, - ) - ode.add_background_rectangle_to_submobjects() - - self.play(Write(ode)) - self.wait() - self.play(FadeOut(ode)) - - def show_evolution_from_a_start_state(self): - state = self.state - dot = self.state_dot - - self.play( - Rotating( - dot, - about_point=dot.get_center() + UR, - rate_func=smooth, - ) - ) - self.wait() - - # Show initial trajectory - state.pendulum.clear_updaters(recursive=False) - self.tie_dot_position_to_state(dot, state) - state.pendulum.start_swinging() - trajectory = self.get_evolving_trajectory(dot) - self.add(trajectory) - for x in range(20): - self.wait() - - # Talk through start - trajectory.suspend_updating() - state.pendulum.end_swinging() - dot.clear_updaters() - self.tie_state_to_dot_position(state, dot) - - alphas = np.linspace(0, 0.1, 1000) - index = np.argmin([ - trajectory.point_from_proportion(a)[1] - for a in alphas - ]) - alpha = alphas[index] - sub_traj = trajectory.copy() - sub_traj.suspend_updating() - sub_traj.pointwise_become_partial(trajectory, 0, alpha) - sub_traj.match_style(trajectory) - sub_traj.set_stroke(width=3) - - self.wait() - self.play(dot.move_to, sub_traj.get_start()) - self.wait() - self.play( - ShowCreation(sub_traj), - UpdateFromFunc( - dot, lambda d: d.move_to(sub_traj.get_end()) - ), - run_time=6, - ) - self.wait() - - # Comment on physical velocity vs. space position - v_vect = state.pendulum.velocity_vector - v_line_copy = self.v_line.copy() - v_line_copy.clear_updaters() - v_line_copy.set_stroke(PINK, 5) - td_label = self.plane.axis_labels[1] - y_axis_copy = self.plane.y_axis.copy() - y_axis_copy.submobjects = [] - y_axis_copy.match_style(v_line_copy) - - self.play(ShowCreationThenFadeAround(v_vect)) - self.play( - ShowCreationThenFadeAround(td_label), - ShowCreationThenFadeOut(y_axis_copy) - ) - self.play(ShowCreationThenFadeOut(v_line_copy)) - self.wait() - - # Abstract vs. physical - abstract = TextMobject("Abstract") - abstract.add_background_rectangle() - abstract.scale(2) - abstract.to_corner(UR) - physical = TextMobject("Physical") - physical.next_to(state.get_top(), DOWN) - - self.play( - ApplyMethod( - self.plane.set_stroke, YELLOW, 0.5, - rate_func=there_and_back, - lag_ratio=0.2, - ), - FadeInFromDown(abstract), - Animation(state), - ) - self.wait() - self.play(FadeInFromDown(physical)) - self.wait() - - # Continue on spiral - sub_traj.resume_updating() - state.pendulum.clear_updaters(recursive=False) - state.pendulum.start_swinging() - dot.clear_updaters() - self.tie_dot_position_to_state(dot, state) - self.wait(20) - - # - def get_initial_thetas(self): - angle = 3 * PI / 4 - return np.linspace(-angle, angle, self.n_thetas) - - def get_initial_omegas(self): - return np.linspace(-1.5, 1.5, self.n_omegas) - - def get_state(self, pendulum): - return (pendulum.get_theta(), pendulum.get_omega()) - - def get_state_point(self, pendulum): - return self.plane.coords_to_point( - *self.get_state(pendulum) - ) - - def get_flexible_state_picture(self): - buff = MED_SMALL_BUFF - height = FRAME_HEIGHT / 2 - buff - rect = Square( - side_length=height, - stroke_color=WHITE, - stroke_width=2, - fill_color="#111111", - fill_opacity=1, - ) - rect.to_corner(UL, buff=buff / 2) - pendulum = Pendulum( - top_point=rect.get_center(), - **self.big_pendulum_config - ) - pendulum.fixed_point_tracker.add_updater( - lambda m: m.move_to(rect.get_center()) - ) - - state = VGroup(rect, pendulum) - state.rect = rect - state.pendulum = pendulum - return state - - def get_state_dot(self, state): - dot = Dot(color=PINK) - dot.move_to(self.get_state_point(state.pendulum)) - return dot - - def get_state_controlling_dot(self, state): - dot = self.get_state_dot(state) - self.tie_state_to_dot_position(state, dot) - return dot - - def tie_state_to_dot_position(self, state, dot): - def update_pendulum(pend): - theta, omega = self.plane.point_to_coords( - dot.get_center() - ) - pend.set_theta(theta) - pend.set_omega(omega) - return pend - state.pendulum.add_updater(update_pendulum) - state.pendulum.get_arc_angle_theta = \ - lambda: self.plane.x_axis.point_to_number(dot.get_center()) - return self - - def tie_dot_position_to_state(self, dot, state): - dot.add_updater(lambda d: d.move_to( - self.get_state_point(state.pendulum) - )) - return dot - - def get_tracking_line(self, point, axis, color=WHITE): - number = axis.point_to_number(point) - axis_point = axis.number_to_point(number) - return DashedLine( - axis_point, point, - dash_length=0.025, - color=color, - ) - - def get_tracking_h_line(self, point): - return self.get_tracking_line( - point, self.plane.y_axis, WHITE, - ) - - def get_tracking_v_line(self, point): - return self.get_tracking_line( - point, self.plane.x_axis, YELLOW, - ) - - def get_evolving_trajectory(self, mobject): - trajectory = VMobject() - trajectory.start_new_path(mobject.get_center()) - trajectory.set_stroke(RED, 1) - - def update_trajectory(traj): - point = mobject.get_center() - if get_norm(trajectory.points[-1] == point) > 0.05: - traj.add_smooth_curve_to(point) - trajectory.add_updater(update_trajectory) - return trajectory - - -class IntroduceVectorField(VisualizeStates): - CONFIG = { - "vector_field_config": { - "max_magnitude": 3, - # "delta_x": 2, - # "delta_y": 2, - }, - "big_pendulum_config": { - "initial_theta": -80 * DEGREES, - "omega": 1, - } - } - - def construct(self): - self.initialize_plane() - self.add_flexible_state() - self.initialize_vector_field() - self.add_equation() - self.preview_vector_field() - self.write_vector_derivative() - self.interpret_first_coordinate() - self.interpret_second_coordinate() - self.show_full_vector_field() - self.show_trajectory() - - def initialize_plane(self): - super().initialize_plane() - self.add(self.plane) - - def initialize_vector_field(self): - self.vector_field = VectorField( - self.vector_field_func, - **self.vector_field_config, - ) - self.vector_field.sort(get_norm) - - def add_flexible_state(self): - self.state = self.get_flexible_state_picture() - self.add(self.state) - - def add_equation(self): - ode = get_ode() - ode.set_width(self.state.get_width() - MED_LARGE_BUFF) - ode.next_to(self.state.get_top(), DOWN, SMALL_BUFF) - thetas = ode.get_parts_by_tex("\\theta") - thetas[0].set_color(RED) - thetas[1].set_color(YELLOW) - ode_word = TextMobject("Differential equation") - ode_word.match_width(ode) - ode_word.next_to(ode, DOWN) - - self.play( - FadeInFrom(ode, 0.5 * DOWN), - FadeInFrom(ode_word, 0.5 * UP), - ) - - self.ode = ode - self.ode_word = ode_word - - def preview_vector_field(self): - vector_field = self.vector_field - - growth = LaggedStartMap( - GrowArrow, vector_field, - run_time=3, - lag_ratio=0.01, - ) - self.add( - growth.mobject, - vector_field, - self.state, self.ode, self.ode_word - ) - - self.play(growth) - self.wait() - self.play(FadeOut(vector_field)) - self.remove(growth.mobject) - - def write_vector_derivative(self): - state = self.state - plane = self.plane - - dot = self.get_state_dot(state) - - # Vector - vect = Arrow( - plane.coords_to_point(0, 0), - dot.get_center(), - buff=0, - color=dot.get_color() - ) - vect_sym, d_vect_sym = [ - self.get_vector_symbol( - "{" + a + "\\theta}(t)", - "{" + b + "\\theta}(t)", - ) - for a, b in [("", "\\dot"), ("\\dot", "\\ddot")] - ] - # vect_sym.get_entries()[1][0][1].set_color(YELLOW) - # d_vect_sym.get_entries()[0][0][1].set_color(YELLOW) - # d_vect_sym.get_entries()[1][0][1].set_color(RED) - vect_sym.next_to(vect.get_end(), UP, MED_LARGE_BUFF) - time_inputs = VGroup(*[ - e[-1][-2] for e in vect_sym.get_entries() - ]) - - # Derivative - ddt = TexMobject("d \\over dt") - ddt.set_height(0.9 * vect_sym.get_height()) - ddt.next_to(vect_sym, LEFT) - ddt.set_stroke(BLACK, 5, background=True) - equals = TexMobject("=") - equals.add_background_rectangle() - equals.next_to(vect_sym, RIGHT, SMALL_BUFF) - d_vect_sym.next_to(equals, RIGHT, SMALL_BUFF) - - # Little vector - angle_tracker = ValueTracker(0) - mag_tracker = ValueTracker(0.75) - d_vect = always_redraw( - lambda: Vector( - rotate_vector( - mag_tracker.get_value() * RIGHT, - angle_tracker.get_value(), - ), - color=WHITE - ).shift(dot.get_center()), - ) - d_vect_magnitude_factor_tracker = ValueTracker(2) - real_d_vect = always_redraw( - lambda: self.vector_field.get_vector( - dot.get_center() - ).scale( - d_vect_magnitude_factor_tracker.get_value(), - about_point=dot.get_center() - ) - ) - - # Show vector - self.play(TransformFromCopy(state[1], vect)) - self.play(FadeInFromDown(vect_sym)) - self.wait() - self.play(ReplacementTransform(vect, dot)) - self.wait() - self.play(LaggedStartMap( - ShowCreationThenFadeAround, time_inputs, - lag_ratio=0.1, - )) - self.wait() - - # Write Derivative - self.play(Write(ddt)) - self.play( - plane.y_axis.numbers.fade, 1, - FadeInFrom(equals, LEFT), - TransformFromCopy(vect_sym, d_vect_sym) - ) - self.wait() - - # Show as little vector - equation_group = VGroup( - ddt, vect_sym, equals, d_vect_sym - ) - self.play( - # equation_group.shift, 4 * DOWN, - equation_group.to_edge, RIGHT, LARGE_BUFF, - GrowArrow(d_vect), - ) - self.wait() - self.play(angle_tracker.set_value, 120 * DEGREES) - self.play(mag_tracker.set_value, 1.5) - self.wait() - - # Highlight new vector - self.play( - ShowCreationThenFadeAround(d_vect_sym), - FadeOut(d_vect) - ) - self.wait() - self.play( - TransformFromCopy(d_vect_sym, real_d_vect), - dot.set_color, WHITE, - ) - self.wait() - - # Take a walk - trajectory = VMobject() - trajectory.start_new_path(dot.get_center()) - dt = 0.01 - for x in range(130): - p = trajectory.points[-1] - dp_dt = self.vector_field_func(p) - trajectory.add_smooth_curve_to(p + dp_dt * dt) - self.tie_state_to_dot_position(state, dot) - self.play( - MoveAlongPath(dot, trajectory), - run_time=5, - rate_func=bezier([0, 0, 1, 1]), - ) - - self.state_dot = dot - self.d_vect = real_d_vect - self.equation_group = equation_group - self.d_vect_magnitude_factor_tracker = d_vect_magnitude_factor_tracker - - def interpret_first_coordinate(self): - equation = self.equation_group - ddt, vect_sym, equals, d_vect_sym = equation - dot = self.state_dot - - first_components_copy = VGroup( - vect_sym.get_entries()[0], - d_vect_sym.get_entries()[0], - ).copy() - rect = SurroundingRectangle(first_components_copy) - rect.set_stroke(YELLOW, 2) - - equation.save_state() - - self.play( - ShowCreation(rect), - equation.fade, 0.5, - Animation(first_components_copy), - ) - self.wait() - dot.save_state() - self.play(dot.shift, 2 * UP) - self.wait() - self.play(dot.shift, 6 * DOWN) - self.wait() - self.play(dot.restore) - self.wait() - - self.play( - equation.restore, - FadeOut(rect), - ) - self.remove(first_components_copy) - - def interpret_second_coordinate(self): - equation = self.equation_group - ddt, vect_sym, equals, d_vect_sym = equation - - second_components = VGroup( - vect_sym.get_entries()[1], - d_vect_sym.get_entries()[1], - ) - rect = SurroundingRectangle(second_components) - rect.set_stroke(YELLOW, 2) - - expanded_derivative = self.get_vector_symbol( - "{\\dot\\theta}(t)", - "-\\mu {\\dot\\theta}(t)" + - "-(g / L) \\sin\\big({\\theta}(t)\\big)", - ) - expanded_derivative.move_to(d_vect_sym) - expanded_derivative.to_edge(RIGHT, MED_SMALL_BUFF) - equals2 = TexMobject("=") - equals2.next_to(expanded_derivative, LEFT, SMALL_BUFF) - - equation.save_state() - self.play( - ShowCreation(rect), - ) - self.wait() - self.play( - FadeInFrom(expanded_derivative, LEFT), - FadeIn(equals2), - equation.next_to, equals2, LEFT, SMALL_BUFF, - MaintainPositionRelativeTo(rect, equation), - VFadeOut(rect), - ) - self.wait() - - self.full_equation = VGroup( - *equation, equals2, expanded_derivative, - ) - - def show_full_vector_field(self): - vector_field = self.vector_field - state = self.state - ode = self.ode - ode_word = self.ode_word - equation = self.full_equation - d_vect = self.d_vect - dot = self.state_dot - - equation.generate_target() - equation.target.scale(0.7) - equation.target.to_edge(DOWN, LARGE_BUFF) - equation.target.to_edge(LEFT, MED_SMALL_BUFF) - equation_rect = BackgroundRectangle(equation.target) - - growth = LaggedStartMap( - GrowArrow, vector_field, - run_time=3, - lag_ratio=0.01, - ) - self.add( - growth.mobject, - state, ode, ode_word, - equation_rect, equation, dot, - d_vect, - ) - self.play( - growth, - FadeIn(equation_rect), - MoveToTarget(equation), - self.d_vect_magnitude_factor_tracker.set_value, 1, - ) - - def show_trajectory(self): - state = self.state - dot = self.state_dot - - state.pendulum.clear_updaters(recursive=False) - self.tie_dot_position_to_state(dot, state) - state.pendulum.start_swinging() - - trajectory = self.get_evolving_trajectory(dot) - trajectory.set_stroke(WHITE, 3) - - self.add(trajectory, dot) - self.wait(25) - - # - def get_vector_symbol(self, tex1, tex2): - t2c = { - "{\\theta}": BLUE, - "{\\dot\\theta}": YELLOW, - "{\\omega}": YELLOW, - "{\\ddot\\theta}": RED, - } - return get_vector_symbol( - tex1, tex2, - element_to_mobject_config={ - "tex_to_color_map": t2c, - } - ).scale(0.9) - - def vector_field_func(self, point): - x, y = self.plane.point_to_coords(point) - mu, g, L = [ - self.big_pendulum_config.get(key) - for key in ["damping", "gravity", "length"] - ] - return pendulum_vector_field_func( - x * RIGHT + y * UP, - mu=mu, g=g, L=L - ) - - def ask_about_change(self): - state = self.state - - dot = self.get_state_dot(state) - d_vect = Vector(0.75 * RIGHT, color=WHITE) - d_vect.shift(dot.get_center()) - q_mark = always_redraw( - lambda: TexMobject("?").move_to( - d_vect.get_end() + 0.4 * rotate_vector( - d_vect.get_vector(), 90 * DEGREES, - ), - ) - ) - - self.play(TransformFromCopy(state[1], dot)) - self.tie_state_to_dot_position(state, dot) - self.play( - GrowArrow(d_vect), - FadeInFromDown(q_mark) - ) - for x in range(4): - angle = 90 * DEGREES - self.play( - Rotate( - d_vect, angle, - about_point=d_vect.get_start(), - ) - ) - self.play( - dot.shift, - 0.3 * d_vect.get_vector(), - rate_func=there_and_back, - ) - - -class ShowPendulumPhaseFlow(IntroduceVectorField): - CONFIG = { - "coordinate_plane_config": { - "x_axis_config": { - "unit_size": 0.8, - }, - "x_max": 9, - "x_min": -9, - }, - "flow_time": 20, - } - - def construct(self): - self.initialize_plane() - self.initialize_vector_field() - plane = self.plane - field = self.vector_field - self.add(plane, field) - - stream_lines = StreamLines( - field.func, - delta_x=0.3, - delta_y=0.3, - ) - animated_stream_lines = AnimatedStreamLines( - stream_lines, - line_anim_class=ShowPassingFlashWithThinningStrokeWidth, - ) - - self.add(animated_stream_lines) - self.wait(self.flow_time) - - -class ShowHighVelocityCase(ShowPendulumPhaseFlow, MovingCameraScene): - CONFIG = { - "coordinate_plane_config": { - "x_max": 15, - "x_min": -15, - }, - "vector_field_config": { - "x_max": 15, - }, - "big_pendulum_config": { - "max_velocity_vector_length_to_length_ratio": 1, - }, - "run_time": 25, - "initial_theta": 0, - "initial_theta_dot": 4, - "frame_shift_vect": TAU * RIGHT, - } - - def setup(self): - MovingCameraScene.setup(self) - - def construct(self): - self.initialize_plane_and_field() - self.add_flexible_state() - self.show_high_vector() - self.show_trajectory() - - def initialize_plane_and_field(self): - self.initialize_plane() - self.add(self.plane) - self.initialize_vector_field() - self.add(self.vector_field) - - def add_flexible_state(self): - super().add_flexible_state() - state = self.state - plane = self.plane - - state.to_edge(DOWN, buff=SMALL_BUFF), - start_point = plane.coords_to_point( - self.initial_theta, - self.initial_theta_dot, - ) - dot = self.get_state_controlling_dot(state) - 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 - frame = self.camera_frame - dot = self.dot - start_point = self.start_point - - traj = self.get_trajectory(start_point, self.run_time) - - self.add(traj, dot) - anims = [ - ShowCreation( - traj, - rate_func=linear, - ), - UpdateFromFunc( - dot, lambda d: d.move_to(traj.points[-1]) - ), - ] - if get_norm(self.frame_shift_vect) > 0: - anims += [ - ApplyMethod( - frame.shift, self.frame_shift_vect, - rate_func=squish_rate_func( - smooth, 0, 0.3, - ) - ), - MaintainPositionRelativeTo(state.rect, frame), - ] - self.play(*anims, run_time=total_time) - - def get_trajectory(self, start_point, time, dt=0.1, added_steps=100): - field = self.vector_field - traj = VMobject() - traj.start_new_path(start_point) - for x in range(int(time / dt)): - last_point = traj.points[-1] - for y in range(added_steps): - dp_dt = field.func(last_point) - last_point += dp_dt * dt / added_steps - traj.add_smooth_curve_to(last_point) - traj.make_smooth() - traj.set_stroke(WHITE, 2) - return traj - - -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") - lil_rect = SurroundingRectangle(mu, buff=0.5 * SMALL_BUFF) - lil_rect.stretch(1.2, 1, about_edge=DOWN) - lil_rect.set_stroke(PINK, 2) - - interval = UnitInterval() - interval.add_numbers( - *np.arange(0, 1.2, 0.2) - ) - interval.next_to(ode, UP, LARGE_BUFF) - big_rect_seed = SurroundingRectangle(interval, buff=MED_SMALL_BUFF) - big_rect_seed.stretch(1.5, 1, about_edge=DOWN) - big_rect_seed.stretch(1.2, 0) - big_rect = VGroup(*[ - DashedLine(v1, v2) - for v1, v2 in adjacent_pairs(big_rect_seed.get_vertices()) - ]) - big_rect.set_stroke(PINK, 2) - - arrow = Arrow( - lil_rect.get_top(), - big_rect_seed.point_from_proportion(0.65), - buff=SMALL_BUFF, - ) - arrow.match_color(lil_rect) - - mu_tracker = ValueTracker(0.1) - get_mu = mu_tracker.get_value - - triangle = Triangle( - start_angle=-90 * DEGREES, - stroke_width=0, - fill_opacity=1, - fill_color=WHITE, - ) - triangle.set_height(0.2) - triangle.add_updater(lambda t: t.next_to( - interval.number_to_point(get_mu()), - UP, buff=0, - )) - - equation = VGroup( - TexMobject("\\mu = "), - DecimalNumber(), - ) - equation.add_updater( - lambda e: e.arrange(RIGHT).next_to( - triangle, UP, SMALL_BUFF, - ).shift(0.4 * RIGHT) - ) - equation[-1].add_updater( - lambda d: d.set_value(get_mu()).shift(0.05 * UL) - ) - - self.add(ode) - self.play(ShowCreation(lil_rect)) - self.play( - GrowFromPoint(interval, mu.get_center()), - GrowFromPoint(triangle, mu.get_center()), - GrowFromPoint(equation, mu.get_center()), - TransformFromCopy(lil_rect, big_rect), - ShowCreation(arrow) - ) - self.wait() - self.play(mu_tracker.set_value, 0.9, run_time=5) - self.wait() - - -class TweakMuInVectorField(ShowPendulumPhaseFlow): - def construct(self): - self.initialize_plane() - plane = self.plane - self.add(plane) - - mu_tracker = ValueTracker(0.1) - get_mu = mu_tracker.get_value - - def vector_field_func(p): - x, y = plane.point_to_coords(p) - mu = get_mu() - g = self.big_pendulum_config.get("gravity") - L = self.big_pendulum_config.get("length") - return pendulum_vector_field_func( - x * RIGHT + y * UP, - mu=mu, g=g, L=L - ) - - def get_vector_field(): - return VectorField( - vector_field_func, - **self.vector_field_config, - ) - - field = always_redraw(get_vector_field) - self.add(field) - - self.play( - mu_tracker.set_value, 0.9, - run_time=5, - ) - field.suspend_updating() - - stream_lines = StreamLines( - field.func, - delta_x=0.3, - delta_y=0.3, - ) - animated_stream_lines = AnimatedStreamLines( - stream_lines, - line_anim_class=ShowPassingFlashWithThinningStrokeWidth, - ) - self.add(animated_stream_lines) - self.wait(self.flow_time) - - -class HighAmplitudePendulum(ShowHighVelocityCase): - CONFIG = { - "big_pendulum_config": { - "damping": 0.02, - }, - "initial_theta": 175 * DEGREES, - "initial_theta_dot": 0, - "frame_shift_vect": 0 * RIGHT, - } - - def construct(self): - self.initialize_plane_and_field() - self.add_flexible_state() - self.show_trajectory() - - -class SpectrumOfStartingStates(ShowHighVelocityCase): - CONFIG = { - "run_time": 15, - } - - def construct(self): - self.initialize_plane_and_field() - self.vector_field.set_opacity(0.5) - self.show_many_trajectories() - - def show_many_trajectories(self): - plane = self.plane - - delta_x = 0.5 - delta_y = 0.5 - n = 20 - - start_points = [ - plane.coords_to_point(x, y) - for x in np.linspace(PI - delta_x, PI + delta_x, n) - for y in np.linspace(-delta_y, delta_y, n) - ] - start_points.sort( - key=lambda p: np.dot(p, UL) - ) - time = self.run_time - - # Count points - dots = VGroup(*[ - Dot(sp, radius=0.025) - for sp in start_points - ]) - dots.set_color_by_gradient(PINK, BLUE, YELLOW) - words = TextMobject( - "Spectrum of\\\\", "initial conditions" - ) - words.set_stroke(BLACK, 5, background=True) - words.next_to(dots, UP) - - self.play( - # ShowIncreasingSubsets(dots, run_time=2), - LaggedStartMap( - FadeInFromLarge, dots, - lambda m: (m, 10), - run_time=2 - ), - FadeInFromDown(words), - ) - self.wait() - - trajs = VGroup() - for sp in start_points: - trajs.add( - self.get_trajectory( - sp, time, - added_steps=10, - ) - ) - for traj, dot in zip(trajs, dots): - traj.set_stroke(dot.get_color(), 1) - - def update_dots(ds): - for d, t in zip(ds, trajs): - d.move_to(t.points[-1]) - return ds - dots.add_updater(update_dots) - - self.add(dots, trajs, words) - self.play( - ShowCreation( - trajs, - lag_ratio=0, - ), - rate_func=linear, - run_time=time, - ) - self.wait() - - -class AskAboutStability(ShowHighVelocityCase): - CONFIG = { - "initial_theta": 60 * DEGREES, - "initial_theta_dot": 1, - } - - def construct(self): - self.initialize_plane_and_field() - self.add_flexible_state() - self.show_fixed_points() - self.label_fixed_points() - self.ask_about_stability() - self.show_nudges() - - def show_fixed_points(self): - state1 = self.state - plane = self.plane - dot1 = self.dot - - state2 = self.get_flexible_state_picture() - state2.to_corner(DR, buff=SMALL_BUFF) - dot2 = self.get_state_controlling_dot(state2) - dot2.set_color(BLUE) - - fp1 = plane.coords_to_point(0, 0) - fp2 = plane.coords_to_point(PI, 0) - - self.play( - dot1.move_to, fp1, - run_time=3, - ) - self.wait() - self.play(FadeIn(state2)) - self.play( - dot2.move_to, fp2, - path_arc=-30 * DEGREES, - run_time=2, - ) - self.wait() - - self.state1 = state1 - self.state2 = state2 - self.dot1 = dot1 - self.dot2 = dot2 - - def label_fixed_points(self): - dots = VGroup(self.dot1, self.dot2) - - label = TextMobject("Fixed points") - label.scale(1.5) - label.set_stroke(BLACK, 5, background=True) - label.next_to(dots, UP, buff=2) - label.shift(SMALL_BUFF * DOWN) - - arrows = VGroup(*[ - Arrow( - label.get_bottom(), dot.get_center(), - color=dot.get_color(), - ) - for dot in dots - ]) - - self.play( - self.vector_field.set_opacity, 0.5, - FadeInFromDown(label) - ) - self.play(ShowCreation(arrows)) - self.wait(2) - - self.to_fade = VGroup(label, arrows) - - def ask_about_stability(self): - question = TextMobject("Stable?") - question.scale(2) - question.shift(FRAME_WIDTH * RIGHT / 4) - question.to_edge(UP) - question.set_stroke(BLACK, 5, background=True) - - self.play(Write(question)) - self.play(FadeOut(self.to_fade)) - - def show_nudges(self): - dots = VGroup(self.dot1, self.dot2) - time = 20 - - self.play(*[ - ApplyMethod( - dot.shift, 0.1 * UL, - rate_func=rush_from, - ) - for dot in dots - ]) - - trajs = VGroup() - for dot in dots: - traj = self.get_trajectory( - dot.get_center(), - time, - ) - traj.set_stroke(dot.get_color(), 2) - trajs.add(traj) - - def update_dots(ds): - for t, d in zip(trajs, ds): - d.move_to(t.points[-1]) - dots.add_updater(update_dots) - self.add(trajs, dots) - self.play( - ShowCreation(trajs, lag_ratio=0), - rate_func=linear, - run_time=time - ) - self.wait() - - -class LovePhaseSpace(ShowHighVelocityCase): - CONFIG = { - "vector_field_config": { - "max_magnitude": 4, - # "delta_x": 2, - # "delta_y": 2, - }, - "a": 0.5, - "b": 0.3, - "mu": 0.2, - } - - def construct(self): - self.setup_plane() - self.add_equations() - self.show_vector_field() - self.show_example_trajectories() - self.add_resistance_term() - self.show_new_trajectories() - - def setup_plane(self): - plane = self.plane = NumberPlane() - plane.add_coordinates() - self.add(plane) - - h1, h2 = hearts = VGroup(*[ - get_heart_var(i) - for i in (1, 2) - ]) - hearts.scale(0.5) - hearts.set_stroke(BLACK, 5, background=True) - - h1.next_to(plane.x_axis.get_right(), UL, SMALL_BUFF) - h2.next_to(plane.y_axis.get_top(), DR, SMALL_BUFF) - for h in hearts: - h.shift_onto_screen(buff=MED_SMALL_BUFF) - plane.add(hearts) - - self.axis_hearts = hearts - - def add_equations(self): - equations = VGroup( - get_love_equation1(), - get_love_equation2(), - ) - equations.scale(0.5) - equations.arrange( - DOWN, - aligned_edge=LEFT, - buff=MED_LARGE_BUFF - ) - equations.to_corner(UL) - equations.add_background_rectangle_to_submobjects() - # for eq in equations: - # eq.add_background_rectangle_to_submobjects() - - self.add(equations) - self.equations = equations - - def show_vector_field(self): - field = VectorField( - lambda p: np.array([ - self.a * p[1], -self.b * p[0], 0 - ]), - **self.vector_field_config - ) - field.sort(get_norm) - x_range = np.arange(-7, 7.5, 0.5) - y_range = np.arange(-4, 4.5, 0.5) - x_axis_arrows = VGroup(*[ - field.get_vector([x, 0, 0]) - for x in x_range - ]) - y_axis_arrows = VGroup(*[ - field.get_vector([0, y, 0]) - for y in y_range - ]) - axis_arrows = VGroup(*x_axis_arrows, *y_axis_arrows) - - axis_arrows.save_state() - for arrow in axis_arrows: - real_len = get_norm(field.func(arrow.get_start())) - arrow.scale( - 0.5 * real_len / arrow.get_length(), - about_point=arrow.get_start() - ) - - self.play( - LaggedStartMap(GrowArrow, x_axis_arrows), - ) - self.play( - LaggedStartMap(GrowArrow, y_axis_arrows), - ) - self.wait() - self.add(field, self.equations, self.axis_hearts) - self.play( - axis_arrows.restore, - # axis_arrows.fade, 1, - ShowCreation(field), - run_time=3 - ) - self.remove(axis_arrows) - self.wait() - - self.field = self.vector_field = field - - def show_example_trajectories(self): - n_points = 20 - total_time = 30 - - start_points = self.start_points = [ - 2.5 * np.random.random() * rotate_vector( - RIGHT, - TAU * np.random.random() - ) - for x in range(n_points) - ] - dots = VGroup(*[Dot(sp) for sp in start_points]) - dots.set_color_by_gradient(BLUE, WHITE) - - words = TextMobject("Possible initial\\\\", "conditions") - words.scale(1.5) - words.add_background_rectangle_to_submobjects() - words.set_stroke(BLACK, 5, background=True) - words.shift(FRAME_WIDTH * RIGHT / 4) - words.to_edge(UP) - self.possibility_words = words - - self.play( - LaggedStartMap( - FadeInFromLarge, dots, - lambda m: (m, 5) - ), - FadeInFromDown(words) - ) - - trajs = VGroup(*[ - self.get_trajectory( - sp, total_time, - added_steps=10, - ) - for sp in start_points - ]) - trajs.set_color_by_gradient(BLUE, WHITE) - - dots.trajs = trajs - - def update_dots(ds): - for d, t in zip(ds, ds.trajs): - d.move_to(t.points[-1]) - dots.add_updater(update_dots) - - self.add(trajs, dots) - self.play( - ShowCreation( - trajs, - lag_ratio=0, - run_time=10, - rate_func=linear, - ) - ) - - self.trajs = trajs - self.dots = dots - - def add_resistance_term(self): - added_term = VGroup( - TexMobject("-\\mu"), - get_heart_var(2).scale(0.5), - ) - added_term.arrange(RIGHT, buff=SMALL_BUFF) - equation2 = self.equations[1] - equation2.generate_target() - br, deriv, eq, neg_b, h1 = equation2.target - added_term.next_to(eq, RIGHT, SMALL_BUFF) - added_term.align_to(h1, DOWN) - VGroup(neg_b, h1).next_to( - added_term, RIGHT, SMALL_BUFF, - aligned_edge=DOWN, - ) - br.stretch(1.2, 0, about_edge=LEFT) - - brace = Brace(added_term, DOWN, buff=SMALL_BUFF) - words = brace.get_text( - "``Resistance'' term" - ) - words.set_stroke(BLACK, 5, background=True) - words.add_background_rectangle() - - self.add(equation2, added_term) - self.play( - MoveToTarget(equation2), - FadeInFromDown(added_term), - GrowFromCenter(brace), - Write(words), - ) - self.play(ShowCreationThenFadeAround(added_term)) - - equation2.add(added_term, brace, words) - - def show_new_trajectories(self): - dots = self.dots - trajs = self.trajs - field = self.field - - new_field = VectorField( - lambda p: np.array([ - self.a * p[1], - -self.mu * p[1] - self.b * p[0], - 0 - ]), - **self.vector_field_config - ) - new_field.sort(get_norm) - - field.generate_target() - for vect in field.target: - vect.become(new_field.get_vector(vect.get_start())) - - self.play(*map( - FadeOut, - [trajs, dots, self.possibility_words] - )) - self.play(MoveToTarget(field)) - self.vector_field = new_field - - total_time = 30 - new_trajs = VGroup(*[ - self.get_trajectory( - sp, total_time, - added_steps=10, - ) - for sp in self.start_points - ]) - new_trajs.set_color_by_gradient(BLUE, WHITE) - dots.trajs = new_trajs - - self.add(new_trajs, dots) - self.play( - ShowCreation( - new_trajs, - lag_ratio=0, - run_time=10, - rate_func=linear, - ), - ) - self.wait() - - -class TakeManyTinySteps(IntroduceVectorField): - CONFIG = { - "initial_theta": 60 * DEGREES, - "initial_theta_dot": 0, - "initial_theta_tex": "\\pi / 3", - "initial_theta_dot_tex": "0", - } - - def construct(self): - self.initialize_plane_and_field() - self.take_many_time_steps() - - def initialize_plane_and_field(self): - self.initialize_plane() - self.initialize_vector_field() - field = self.vector_field - field.set_opacity(0.35) - self.add(self.plane, field) - - def take_many_time_steps(self): - self.setup_trackers() - delta_t_tracker = self.delta_t_tracker - get_delta_t = delta_t_tracker.get_value - time_tracker = self.time_tracker - get_t = time_tracker.get_value - - traj = always_redraw( - lambda: self.get_time_step_trajectory( - get_delta_t(), - get_t(), - self.initial_theta, - self.initial_theta_dot, - ) - ) - vectors = always_redraw( - lambda: self.get_path_vectors( - get_delta_t(), - get_t(), - self.initial_theta, - self.initial_theta_dot, - ) - ) - - # Labels - labels, init_labels = self.get_labels(get_t, get_delta_t) - t_label, dt_label = labels - - theta_t_label = TexMobject("\\theta(t)...\\text{ish}") - theta_t_label.scale(0.75) - theta_t_label.add_updater(lambda m: m.next_to( - vectors[-1].get_end(), - vectors[-1].get_vector(), - SMALL_BUFF, - )) - - self.add(traj, vectors, init_labels, labels) - time_tracker.set_value(0) - target_time = 10 - self.play( - VFadeIn(theta_t_label), - ApplyMethod( - time_tracker.set_value, target_time, - run_time=5, - rate_func=linear, - ) - ) - self.wait() - t_label[-1].clear_updaters() - self.remove(theta_t_label) - target_delta_t = 0.01 - self.play( - delta_t_tracker.set_value, target_delta_t, - run_time=7, - ) - self.wait() - traj.clear_updaters() - vectors.clear_updaters() - - # Show steps - count_tracker = ValueTracker(0) - count = Integer() - count.scale(1.5) - count.to_edge(LEFT) - count.shift(UP + MED_SMALL_BUFF * UR) - count.add_updater(lambda c: c.set_value( - count_tracker.get_value() - )) - count_label = TextMobject("steps") - count_label.scale(1.5) - count_label.add_updater( - lambda m: m.next_to( - count[-1], RIGHT, - submobject_to_align=m[0][0], - aligned_edge=DOWN - ) - ) - - scaled_vectors = vectors.copy() - scaled_vectors.clear_updaters() - for vector in scaled_vectors: - vector.scale( - 1 / vector.get_length(), - about_point=vector.get_start() - ) - vector.set_color(YELLOW) - - def update_scaled_vectors(group): - group.set_opacity(0) - group[min( - int(count.get_value()), - len(group) - 1, - )].set_opacity(1) - - scaled_vectors.add_updater(update_scaled_vectors) - - self.add(count, count_label, scaled_vectors) - self.play( - ApplyMethod( - count_tracker.set_value, - int(target_time / target_delta_t), - rate_func=linear, - ), - run_time=5, - ) - self.play(FadeOut(scaled_vectors)) - self.wait() - - def setup_trackers(self): - self.delta_t_tracker = ValueTracker(0.5) - self.time_tracker = ValueTracker(10) - - def get_labels(self, get_t, get_delta_t): - t_label, dt_label = labels = VGroup(*[ - VGroup( - TexMobject("{} = ".format(s)), - DecimalNumber(0) - ).arrange(RIGHT, aligned_edge=DOWN) - for s in ("t", "{\\Delta t}") - ]) - - dt_label[-1].add_updater( - lambda d: d.set_value(get_delta_t()) - ) - t_label[-1].add_updater( - lambda d: d.set_value( - int(np.ceil(get_t() / get_delta_t())) * get_delta_t() - ) - ) - - init_labels = VGroup( - TexMobject( - "\\theta_0", "=", self.initial_theta_tex, - tex_to_color_map={"\\theta": BLUE}, - ), - TexMobject( - "{\\dot\\theta}_0 =", self.initial_theta_dot_tex, - tex_to_color_map={"{\\dot\\theta}": YELLOW}, - ), - ) - for group in labels, init_labels: - for label in group: - label.scale(1.25) - label.add_background_rectangle() - group.arrange(DOWN) - group.shift(FRAME_WIDTH * RIGHT / 4) - labels.to_edge(UP) - init_labels.shift(2 * DOWN) - - return labels, init_labels - - # - def get_time_step_points(self, delta_t, total_time, theta_0, theta_dot_0): - plane = self.plane - field = self.vector_field - curr_point = plane.coords_to_point( - theta_0, - theta_dot_0, - ) - points = [curr_point] - t = 0 - while t < total_time: - new_point = curr_point + field.func(curr_point) * delta_t - points.append(new_point) - curr_point = new_point - t += delta_t - return points - - def get_time_step_trajectory(self, delta_t, total_time, theta_0, theta_dot_0): - traj = VMobject() - traj.set_points_as_corners( - self.get_time_step_points( - delta_t, total_time, - theta_0, theta_dot_0, - ) - ) - traj.set_stroke(WHITE, 2) - return traj - - def get_path_vectors(self, delta_t, total_time, theta_0, theta_dot_0): - corners = self.get_time_step_points( - delta_t, total_time, - theta_0, theta_dot_0, - ) - result = VGroup() - for a1, a2 in zip(corners, corners[1:]): - vector = Arrow( - a1, a2, buff=0, - ) - vector.match_style( - self.vector_field.get_vector(a1) - ) - result.add(vector) - return result - - -class SetupToTakingManyTinySteps(TakeManyTinySteps): - CONFIG = { - } - - def construct(self): - self.initialize_plane_and_field() - self.show_step() - - def show_step(self): - self.setup_trackers() - get_delta_t = self.delta_t_tracker.get_value - get_t = self.time_tracker.get_value - - labels, init_labels = self.get_labels(get_t, get_delta_t) - t_label, dt_label = labels - - dt_part = dt_label[1][0][:-1].copy() - - init_labels_rect = SurroundingRectangle(init_labels) - init_labels_rect.set_color(PINK) - - field = self.vector_field - point = self.plane.coords_to_point( - self.initial_theta, - self.initial_theta_dot, - ) - dot = Dot(point, color=init_labels_rect.get_color()) - - vector_value = field.func(point) - vector = field.get_vector(point) - vector.scale( - get_norm(vector_value) / vector.get_length(), - about_point=vector.get_start() - ) - scaled_vector = vector.copy() - scaled_vector.scale( - get_delta_t(), - about_point=scaled_vector.get_start() - ) - - v_label = TexMobject("\\vec{\\textbf{v}}") - v_label.set_stroke(BLACK, 5, background=True) - v_label.next_to(vector, LEFT, SMALL_BUFF) - - real_field = field.copy() - for v in real_field: - p = v.get_start() - v.scale( - get_norm(field.func(p)) / v.get_length(), - about_point=p - ) - - self.add(init_labels) - self.play(ShowCreation(init_labels_rect)) - self.play(ReplacementTransform( - init_labels_rect, - dot, - )) - self.wait() - self.add(vector, dot) - self.play( - ShowCreation(vector), - FadeInFrom(v_label, RIGHT), - ) - self.play(FadeInFromDown(dt_label)) - self.wait() - - # - v_label.generate_target() - dt_part.generate_target() - dt_part.target.next_to(scaled_vector, LEFT, SMALL_BUFF) - v_label.target.next_to(dt_part.target, LEFT, SMALL_BUFF) - rect = BackgroundRectangle( - VGroup(v_label.target, dt_part.target) - ) - - self.add(rect, v_label, dt_part) - self.play( - ReplacementTransform(vector, scaled_vector), - FadeIn(rect), - MoveToTarget(v_label), - MoveToTarget(dt_part), - ) - self.add(scaled_vector, dot) - self.wait() - - self.play( - LaggedStart(*[ - Transform( - sm1, sm2, - rate_func=there_and_back_with_pause, - ) - for sm1, sm2 in zip(field, real_field) - ], lag_ratio=0.001, run_time=3) - ) - self.wait() - - -class ShowClutterPrevention(SetupToTakingManyTinySteps): - def construct(self): - self.initialize_plane_and_field() - - # Copied from above scene - field = self.vector_field - real_field = field.copy() - for v in real_field: - p = v.get_start() - v.scale( - get_norm(field.func(p)) / v.get_length(), - about_point=p - ) - - self.play( - LaggedStart(*[ - Transform( - sm1, sm2, - rate_func=there_and_back_with_pause, - ) - for sm1, sm2 in zip(field, real_field) - ], lag_ratio=0.001, run_time=3) - ) - self.wait() - - -class ManyStepsFromDifferentStartingPoints(TakeManyTinySteps): - CONFIG = { - "initial_thetas": np.linspace(0.1, PI - 0.1, 10), - "initial_theta_dot": 0, - } - - def construct(self): - self.initialize_plane_and_field() - self.take_many_time_steps() - - def take_many_time_steps(self): - delta_t_tracker = ValueTracker(0.2) - get_delta_t = delta_t_tracker.get_value - - time_tracker = ValueTracker(10) - get_t = time_tracker.get_value - # traj = always_redraw( - # lambda: VGroup(*[ - # self.get_time_step_trajectory( - # get_delta_t(), - # get_t(), - # theta, - # self.initial_theta_dot, - # ) - # for theta in self.initial_thetas - # ]) - # ) - vectors = always_redraw( - lambda: VGroup(*[ - self.get_path_vectors( - get_delta_t(), - get_t(), - theta, - self.initial_theta_dot, - ) - for theta in self.initial_thetas - ]) - ) - - self.add(vectors) - time_tracker.set_value(0) - self.play( - time_tracker.set_value, 5, - run_time=5, - rate_func=linear, - ) - - -class Thumbnail(IntroduceVectorField): - CONFIG = { - "vector_field_config": { - "delta_x": 0.5, - "delta_y": 0.5, - "max_magnitude": 5, - "length_func": lambda norm: 0.5 * sigmoid(norm), - } - } - - def construct(self): - self.initialize_plane() - self.plane.axes.set_stroke(width=0.5) - self.initialize_vector_field() - - field = self.vector_field - field.set_stroke(width=5) - for vector in field: - vector.set_stroke(width=3) - vector.tip.set_stroke(width=0) - vector.tip.scale(1.5, about_point=vector.get_last_point()) - vector.set_opacity(1) - - title = TextMobject("Differential\\\\", "equations") - title.space_out_submobjects(0.8) - # title.scale(3) - title.set_width(FRAME_WIDTH - 3) - # title.to_edge(UP) - # title[1].to_edge(DOWN) - - subtitle = TextMobject("Studying the unsolvable") - subtitle.set_width(FRAME_WIDTH - 1) - subtitle.set_color(WHITE) - subtitle.to_edge(DOWN, buff=1) - - # title.center() - title.to_edge(UP, buff=1) - title.add(subtitle) - # title.set_stroke(BLACK, 15, background=True) - # title.add_background_rectangle_to_submobjects(opacity=0.5) - title.set_stroke(BLACK, 15, background=True) - subtitle.set_stroke(RED, 2, background=True) - # for part in title: - # part[0].set_fill(opacity=0.25) - # part[0].set_stroke(width=0) - black_parts = VGroup() - for mob in title.family_members_with_points(): - for sp in mob.get_subpaths(): - new_mob = VMobject() - new_mob.set_points(sp) - new_mob.set_fill(BLACK, 0.25) - new_mob.set_stroke(width=0) - black_parts.add(new_mob) - - for vect in field: - for mob in title.family_members_with_points(): - for p in [vect.get_start(), vect.get_end()]: - x, y = p[:2] - x0, y0 = mob.get_corner(DL)[:2] - x1, y1 = mob.get_corner(UR)[:2] - if x0 < x < x1 and y0 < y < y1: - vect.set_opacity(0.25) - vect.tip.set_stroke(width=0) - - self.add(self.plane) - self.add(field) - self.add(black_parts) - self.add(title) diff --git a/active_projects/ode/part1/pi_scenes.py b/active_projects/ode/part1/pi_scenes.py deleted file mode 100644 index ab0d60fa..00000000 --- a/active_projects/ode/part1/pi_scenes.py +++ /dev/null @@ -1,515 +0,0 @@ -from manimlib.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() - 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 TourOfDifferentialEquations(Scene): -# def construct(self): -# 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, - "{\\dot\\theta}": YELLOW, - "{\\ddot\\theta}": RED, - } - } - func = TexMobject( - "{\\theta}(t)", "=", - "\\theta_0", "\\cos(\\sqrt{g / L} \\cdot t)", - **tex_config, - ) - d_func = TexMobject( - "{\\dot\\theta}(t)", "=", - "-\\left(\\sqrt{g / L}\\right)", - "\\theta_0", "\\sin(\\sqrt{g / L} \\cdot t)", - **tex_config, - ) - dd_func = TexMobject( - "{\\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 = get_ode() - arrows = [TexMobject("\\Downarrow") for x in range(2)] - - VGroup(func, d_func, dd_func, ode, *arrows).scale(0.7) - - teacher = self.teacher - you = self.students[2] - - self.student_thinks(ode) - you.add_updater(lambda m: m.look_at(func)) - self.teacher_holds_up(func) - self.wait() - - group = VGroup(arrows[0], d_func, arrows[1], dd_func) - group.arrange(DOWN) - group.move_to(func, DOWN) - - arrow = Arrow( - group.get_corner(UL), - ode.get_top(), - path_arc=PI / 2, - ) - q_marks = VGroup(*[ - TexMobject("?").scale(1.5).next_to( - arrow.point_from_proportion(a), - UP - ) - for a in np.linspace(0.2, 0.8, 5) - ]) - cycle_animation(VFadeInThenOut( - q_marks, - lag_ratio=0.2, - run_time=4, - rate_func=squish_rate_func(smooth, 0, 0.5) - )) - - self.play( - func.next_to, group, UP, - LaggedStartMap( - FadeInFrom, group, - lambda m: (m, UP) - ), - teacher.change, "guilty", - you.change, "sassy", - ) - - rect = SurroundingRectangle( - VGroup(group, func) - ) - dashed_rect = DashedVMobject(rect, num_dashes=75) - animated_rect = AnimatedBoundary(dashed_rect, cycle_rate=1) - - self.wait() - self.add(animated_rect, q_marks) - self.play( - ShowCreation(arrow), - # FadeInFromDown(q_mark), - self.get_student_changes("confused", "confused") - ) - self.wait(4) - self.change_student_modes( - *3 * ["pondering"], - self.teacher.change, "maybe" - ) - 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() - ode.to_corner(UL) - self.add(ode) - morty = self.teacher - - self.student_says( - "Yeah yeah, but how do\\\\" - "you actually \\emph{solve} it?", - student_index=1, - target_mode="sassy", - added_anims=[morty.change, "thinking"], - ) - self.change_student_modes( - "confused", "sassy", "confused", - look_at_arg=ode, - ) - self.wait() - self.teacher_says( - "What do you mean\\\\ by ``solve''?", - target_mode="speaking", - added_anims=[self.get_student_changes( - *3 * ["erm"] - )] - ) - self.play(self.students[1].change, "angry") - self.wait(3) - - -class HungerForExactness(TeacherStudentsScene): - def construct(self): - students = self.students - you = students[2] - teacher = self.teacher - - ode = get_ode() - ode.to_corner(UL) - left_part = ode[:5] - friction_part = ode[5:11] - self.add(ode) - - proposed_solution = TexMobject( - "\\theta_0\\cos((\\sqrt{g/L})t)e^{-\\mu t}" - ) - proposed_solution.next_to( - you.get_corner(UL), UP, buff=0.7 - ) - proposed_solution_rect = SurroundingRectangle( - proposed_solution, buff=MED_SMALL_BUFF, - ) - proposed_solution_rect.set_color(BLUE) - proposed_solution_rect.round_corners() - - solution_p1 = TexMobject( - """ - \\theta(t) = 2\\text{am}\\left( - \\frac{\\sqrt{2g + Lc_1} (t + c_2)}{2\\sqrt{L}}, - \\frac{4g}{2g + Lc_1} - \\right) - """, - ) - solution_p1.to_corner(UL) - solution_p2 = TexMobject( - "c_1, c_2 = \\text{Constants depending on initial conditions}" - ) - solution_p2.set_color(LIGHT_GREY) - solution_p2.scale(0.75) - solution_p3 = TexMobject( - """ - \\text{am}(u, k) = - \\int_0^u \\text{dn}(v, k)\\,dv - """ - ) - solution_p3.name = TextMobject( - "(Jacobi amplitude function)" - ) - solution_p4 = TexMobject( - """ - \\text{dn}(u, k) = - \\sqrt{1 - k^2 \\sin^2(\\phi)} - """ - ) - solution_p4.name = TextMobject( - "(Jacobi elliptic function)" - ) - solution_p5 = TextMobject("Where $\\phi$ satisfies") - solution_p6 = TexMobject( - """ - u = \\int_0^\\phi \\frac{dt}{\\sqrt{1 - k^2 \\sin^2(t)}} - """ - ) - - solution = VGroup( - solution_p1, - solution_p2, - solution_p3, - solution_p4, - solution_p5, - solution_p6, - ) - solution.arrange(DOWN) - solution.scale(0.7) - solution.to_corner(UL, buff=MED_SMALL_BUFF) - solution.set_stroke(width=0, background=True) - - solution.remove(solution_p2) - solution_p1.add(solution_p2) - solution.remove(solution_p5) - solution_p6.add(solution_p5) - - for part in [solution_p3, solution_p4]: - part.name.scale(0.7 * 0.7) - part.name.set_color(LIGHT_GREY) - part.name.next_to(part, RIGHT) - part.add(part.name) - - self.student_says( - "Right, but like,\\\\" - "what \\emph{is} $\\theta(t)$?", - target_mode="sassy", - added_anims=[teacher.change, "guilty"], - ) - self.wait() - self.play( - FadeInFromDown(proposed_solution), - RemovePiCreatureBubble( - you, - target_mode="raise_left_hand", - look_at_arg=proposed_solution, - ), - teacher.change, "pondering", - students[0].change, "pondering", - students[1].change, "hesitant", - ) - self.play(ShowCreation(proposed_solution_rect)) - self.play( - proposed_solution.shift, 3 * RIGHT, - proposed_solution_rect.shift, 3 * RIGHT, - you.change, "raise_right_hand", teacher.eyes, - ) - self.wait(3) - - self.play( - FadeOut(proposed_solution), - FadeOut(proposed_solution_rect), - ode.move_to, self.hold_up_spot, DOWN, - ode.shift, LEFT, - teacher.change, "raise_right_hand", - self.get_student_changes(*3 * ["pondering"]) - ) - self.wait() - ode.save_state() - self.play( - left_part.move_to, friction_part, RIGHT, - left_part.match_y, left_part, - friction_part.to_corner, DR, - friction_part.fade, 0.5, - ) - self.wait() - - modes = ["erm", "sad", "sad", "horrified"] - for part, mode in zip(solution, modes): - self.play( - FadeInFrom(part, UP), - self.get_student_changes( - *3 * [mode], - look_at_arg=part, - ) - ) - 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( - "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) diff --git a/active_projects/ode/part1/shared_constructs.py b/active_projects/ode/part1/shared_constructs.py deleted file mode 100644 index 1dcfddf6..00000000 --- a/active_projects/ode/part1/shared_constructs.py +++ /dev/null @@ -1,118 +0,0 @@ -from manimlib.imports import * - - -Lg_formula_config = { - "tex_to_color_map": { - "\\theta_0": WHITE, - "{L}": BLUE, - "{g}": YELLOW, - }, -} - - -class You(PiCreature): - CONFIG = { - "color": BLUE_C, - } - - -def get_ode(): - tex_config = { - "tex_to_color_map": { - "{\\theta}": BLUE, - "{\\dot\\theta}": RED, - "{\\ddot\\theta}": YELLOW, - "{t}": WHITE, - "{\\mu}": WHITE, - } - } - ode = TexMobject( - "{\\ddot\\theta}({t})", "=", - "-{\\mu} {\\dot\\theta}({t})", - "-{g \\over L} \\sin\\big({\\theta}({t})\\big)", - **tex_config, - ) - 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([ - omega, - -np.sqrt(g / L) * np.sin(theta) - mu * omega, - 0, - ]) - - -def get_vector_symbol(*texs, **kwargs): - config = { - "include_background_rectangle": True, - "bracket_h_buff": SMALL_BUFF, - "bracket_v_buff": SMALL_BUFF, - "element_alignment_corner": ORIGIN, - } - 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 diff --git a/active_projects/ode/part1/staging.py b/active_projects/ode/part1/staging.py deleted file mode 100644 index 823bd1fe..00000000 --- a/active_projects/ode/part1/staging.py +++ /dev/null @@ -1,3028 +0,0 @@ -from manimlib.imports import * -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 - - -class VectorFieldTest(Scene): - def construct(self): - plane = NumberPlane( - # axis_config={"unit_size": 2} - ) - mu_tracker = ValueTracker(1) - field = VectorField( - 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=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, - delta_y=0.5, - ) - animated_stream_lines = AnimatedStreamLines( - stream_lines, - line_anim_class=ShowPassingFlashWithThinningStrokeWidth, - ) - - self.add(plane, field, animated_stream_lines) - self.wait(10) - - -class ShowRect(Scene): - CONFIG = { - "height": 1, - "width": 3, - } - - def construct(self): - rect = Rectangle( - height=self.height, - width=self.width, - ) - rect.set_stroke(YELLOW) - self.play(ShowCreation(rect)) - self.play(FadeOut(rect)) - - -class ShowSquare(ShowRect): - CONFIG = { - "height": 3, - "width": 3, - } - - -class WhenChangeIsEasier(Scene): - def construct(self): - pass - - -class AirResistanceBrace(Scene): - def construct(self): - brace = Brace(Line(ORIGIN, RIGHT), DOWN) - word = TextMobject("Air resistance") - word.next_to(brace, DOWN) - self.play(GrowFromCenter(brace), FadeInFrom(word, UP)) - self.wait() - - -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": { - "stroke_width": 2, - "stroke_color": WHITE, - "fill_opacity": 1, - "fill_color": BLACK, - }, - "camera_config": {"background_color": DARKER_GREY}, - "zoomed_thumbnail_index": 0, - } - - def construct(self): - self.add_title() - self.show_thumbnails() - self.zoom_in_to_one_thumbnail() - # self.show_words() - - def add_title(self): - title = TextMobject( - "A Tourist's Guide \\\\to Differential\\\\Equations" - ) - title.scale(1.5) - title.to_corner(UR) - self.add(title) - - def show_thumbnails(self): - thumbnails = self.thumbnails = Group( - Group(ScreenRectangle(**self.screen_rect_style)), - Group(ScreenRectangle(**self.screen_rect_style)), - Group(ScreenRectangle(**self.screen_rect_style)), - Group(ScreenRectangle(**self.screen_rect_style)), - Group(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], - ]) - line.shift(MED_SMALL_BUFF * LEFT) - for thumbnail, a in zip(thumbnails, np.linspace(0, 1, n)): - thumbnail.move_to(line.point_from_proportion(a)) - dots = TexMobject("\\dots") - dots.next_to(thumbnails[-1], RIGHT) - - self.add_phase_space_preview(thumbnails[0]) - self.add_heat_preview(thumbnails[1]) - self.add_fourier_series(thumbnails[2]) - self.add_matrix_exponent(thumbnails[3]) - self.add_laplace_symbol(thumbnails[4]) - - 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 - ) - self.play(Write(dots)) - self.wait() - - self.thumbnails = thumbnails - - def zoom_in_to_one_thumbnail(self): - self.play( - self.camera_frame.replace, - self.thumbnails[self.zoomed_thumbnail_index], - run_time=3, - ) - self.wait() - - 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() - - # - def add_phase_space_preview(self, thumbnail): - image = ImageMobject("LovePhaseSpace") - image.replace(thumbnail) - thumbnail.add(image) - - def add_heat_preview(self, thumbnail): - image = ImageMobject("HeatSurfaceExample") - image.replace(thumbnail) - thumbnail.add(image) - - def add_matrix_exponent(self, thumbnail): - matrix = IntegerMatrix( - [[3, 1], [4, 1]], - v_buff=MED_LARGE_BUFF, - h_buff=MED_LARGE_BUFF, - bracket_h_buff=SMALL_BUFF, - bracket_v_buff=SMALL_BUFF, - ) - e = TexMobject("e") - t = TexMobject("t") - t.scale(1.5) - t.next_to(matrix, RIGHT, SMALL_BUFF) - e.scale(2) - e.move_to(matrix.get_corner(DL), UR) - group = VGroup(e, matrix, t) - group.set_height(0.7 * thumbnail.get_height()) - randy = Randolph(mode="confused", height=0.75) - randy.next_to(group, LEFT, aligned_edge=DOWN) - randy.look_at(matrix) - group.add(randy) - group.move_to(thumbnail) - thumbnail.add(group) - - def add_fourier_series(self, thumbnail): - colors = [BLUE, GREEN, YELLOW, RED, RED_E, PINK] - - waves = VGroup(*[ - self.get_square_wave_approx(N, color) - for N, color in enumerate(colors) - ]) - waves.set_stroke(width=1.5) - waves.replace(thumbnail, stretch=True) - waves.scale(0.8) - waves.move_to(thumbnail) - thumbnail.add(waves) - - def get_square_wave_approx(self, N, color): - return FunctionGraph( - lambda x: sum([ - (1 / n) * np.sin(n * PI * x) - for n in range(1, 2 * N + 3, 2) - ]), - x_min=0, - x_max=2, - color=color - ) - - def add_laplace_symbol(self, thumbnail): - mob = TexMobject( - "\\mathcal{L}\\left\\{f(t)\\right\\}" - ) - mob.set_width(0.8 * thumbnail.get_width()) - mob.move_to(thumbnail) - thumbnail.add(mob) - - -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): - CONFIG = { - "flash": True, - "add_ball_copies": True, - } - - 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( - lambda p: DOWN, - # delta_x=2, - # delta_y=2, - ) - gravity_field.set_opacity(0.5) - gravity_field.sort_submobjects( - lambda p: -p[1], - ) - self.add(gravity_field) - - def add_title(self): - title = self.title = TextMobject("Gravitational acceleration") - title.scale(1.5) - title.to_edge(UP) - title.add_background_rectangle( - buff=0.05, - opacity=1, - ) - self.play(FadeInFromDown(title)) - - def pulse_gravity_down(self): - field = self.gravity_field - self.play(LaggedStart(*[ - ApplyFunction( - lambda v: v.set_opacity(1).scale(1.2), - vector, - rate_func=there_and_back, - ) - for vector in field - ]), run_time=2, lag_ratio=0.001) - 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): - total_time = 6 - - ball = self.get_ball() - - p0 = 3 * DOWN + 5 * LEFT - v0 = 2.8 * UP + 1.5 * RIGHT - g = 0.9 * DOWN - graph = ParametricFunction( - lambda t: p0 + v0 * t + 0.5 * g * t**2, - t_min=0, - t_max=total_time, - ) - # graph.center().to_edge(DOWN) - dashed_graph = DashedVMobject(graph, num_dashes=60) - dashed_graph.set_stroke(WHITE, 1) - - ball.move_to(graph.get_start()) - randy.add_updater( - lambda m, dt: m.rotate(dt).move_to(ball) - ) - times = np.arange(0, total_time + 1) - - velocity_graph = ParametricFunction( - lambda t: v0 + g * t, - t_min=0, t_max=total_time, - ) - v_point = VectorizedPoint() - v_point.move_to(velocity_graph.get_start()) - - def get_v_vect(): - result = Vector( - v_point.get_location(), - color=RED, - tip_length=0.2, - ) - result.scale(0.5, about_point=result.get_start()) - result.shift(ball.get_center()) - result.set_stroke(width=2, family=False) - return result - v_vect = always_redraw(get_v_vect) - self.add(v_vect) - - flash_rect = FullScreenRectangle( - stroke_width=0, - fill_color=WHITE, - fill_opacity=0.2, - ) - flash = FadeOut( - flash_rect, - 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) - for t1, t2 in zip(times, times[1:]): - v_vect_copy = v_vect.copy() - v_vect_copies.add(v_vect_copy) - ball_copy = ball.copy() - ball_copy.clear_updaters() - ball_copies.add(ball_copy) - - if self.add_ball_copies: - self.add(v_vect_copy) - self.add(ball_copy, ball) - - dashed_graph.save_state() - kw = { - "rate_func": lambda alpha: interpolate( - t1 / total_time, - t2 / total_time, - alpha - ) - } - anims = [ - ShowCreation(dashed_graph, **kw), - MoveAlongPath(ball, graph, **kw), - MoveAlongPath(v_point, velocity_graph, **kw), - ApplyMethod( - time_tracker.increment_value, 1, - rate_func=linear - ), - ] - if self.flash: - anims.append(flash) - self.play(*anims, run_time=1) - dashed_graph.restore() - randy.clear_updaters() - self.play(FadeOut(time_group)) - self.wait() - - self.v_vects = v_vect_copies - - def combine_v_vects(self): - v_vects = self.v_vects.copy() - v_vects.generate_target() - new_center = 2 * DOWN + 2 * LEFT - for vect in v_vects.target: - vect.scale(1.5) - vect.set_stroke(width=2) - vect.shift(new_center - vect.get_start()) - - self.play(MoveToTarget(v_vects)) - - delta_vects = VGroup(*[ - Arrow( - v1.get_end(), - v2.get_end(), - buff=0.01, - color=YELLOW, - ).set_opacity(0.5) - for v1, v2 in zip(v_vects, v_vects[1:]) - ]) - brace = Brace(Line(ORIGIN, UP), RIGHT) - braces = VGroup(*[ - brace.copy().match_height(arrow).next_to( - arrow, RIGHT, buff=0.2 * SMALL_BUFF - ) - for arrow in delta_vects - ]) - amounts = VGroup(*[ - TextMobject("9.8 m/s").scale(0.5).next_to( - brace, RIGHT, SMALL_BUFF - ) - for brace in braces - ]) - - self.play( - FadeOut(self.gravity_field), - FadeIn(delta_vects, lag_ratio=0.1), - ) - self.play( - LaggedStartMap(GrowFromCenter, braces), - LaggedStartMap(FadeInFrom, amounts, lambda m: (m, LEFT)), - ) - 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() - - # - def get_ball(self): - ball = Circle( - stroke_width=1, - stroke_color=WHITE, - fill_color=GREY, - fill_opacity=1, - sheen_factor=1, - sheen_direction=UL, - radius=0.25, - ) - randy = Randolph(mode="pondering") - randy.eyes.set_stroke(BLACK, 0.5) - randy.match_height(ball) - randy.scale(0.75) - randy.move_to(ball) - ball.add(randy) - return ball - - -class ShowSimpleTrajectory(ShowGravityAcceleration): - CONFIG = { - "flash": False, - } - - def construct(self): - self.show_trajectory() - - -class SimpleProjectileEquation(ShowGravityAcceleration): - CONFIG = { - "y0": 0, - "g": 9.8, - "axes_config": { - "x_min": 0, - "x_max": 6, - "x_axis_config": { - "unit_size": 1.5, - "tip_width": 0.15, - }, - "y_min": -30, - "y_max": 35, - "y_axis_config": { - "unit_size": 0.1, - "numbers_with_elongated_ticks": range( - -30, 35, 10 - ), - "tick_size": 0.05, - "numbers_to_show": range(-30, 31, 10), - "tip_width": 0.15, - }, - "center_point": 2 * LEFT, - } - } - - def construct(self): - self.add_axes() - self.setup_trajectory() - - self.show_trajectory() - self.show_equation() - self.solve_for_velocity() - self.solve_for_position() - - def add_axes(self): - axes = self.axes = Axes(**self.axes_config) - axes.set_stroke(width=2) - axes.add_coordinates() - - t_label = TexMobject("t") - t_label.next_to(axes.x_axis.get_right(), UL) - axes.add(t_label) - - self.add(axes) - - def setup_trajectory(self): - axes = self.axes - total_time = self.total_time = 5 - - ball = self.get_ball() - offset_vector = 3 * LEFT - - g = self.g - y0 = self.y0 - v0 = 0.5 * g * total_time - - t_tracker = ValueTracker(0) - get_t = t_tracker.get_value - - # Position - def y_func(t): - return -0.5 * g * t**2 + v0 * t + y0 - - graph_template = axes.get_graph(y_func, x_max=total_time) - graph_template.set_stroke(width=2) - traj_template = graph_template.copy() - traj_template.stretch(0, 0) - traj_template.move_to( - axes.coords_to_point(0, 0), DOWN - ) - traj_template.shift(offset_vector) - traj_template.set_stroke(width=0.5) - - graph = VMobject() - graph.set_stroke(BLUE, 2) - traj = VMobject() - traj.set_stroke(WHITE, 0.5) - graph.add_updater(lambda g: g.pointwise_become_partial( - graph_template, 0, get_t() / total_time - )) - traj.add_updater(lambda t: t.pointwise_become_partial( - traj_template, 0, get_t() / total_time - )) - - def get_ball_point(): - return axes.coords_to_point( - 0, y_func(get_t()) - ) + offset_vector - - f_always(ball.move_to, get_ball_point) - - h_line = always_redraw(lambda: DashedLine( - get_ball_point(), - axes.input_to_graph_point(get_t(), graph_template), - stroke_width=1, - )) - - y_label = TexMobject("y", "(t)") - y_label.set_color_by_tex("y", BLUE) - y_label.add_updater( - lambda m: m.next_to( - graph.get_last_point(), - UR, SMALL_BUFF, - ) - ) - - # Velocity - def v_func(t): - return -g * t + v0 - - def get_v_vect(): - return Vector( - axes.y_axis.unit_size * v_func(get_t()) * UP, - color=RED, - ) - v_vect = always_redraw( - lambda: get_v_vect().shift(get_ball_point()) - ) - v_brace = always_redraw(lambda: Brace(v_vect, LEFT)) - dy_dt_label = TexMobject( - "{d", "y", "\\over dt}", "(t)", - ) - dy_dt_label.scale(0.8) - dy_dt_label.set_color_by_tex("y", BLUE) - y_dot_label = TexMobject("\\dot y", "(t)") - y_dot_label.set_color_by_tex("\\dot y", RED) - for label in dy_dt_label, y_dot_label: - label.add_updater(lambda m: m.next_to( - v_brace, LEFT, SMALL_BUFF, - )) - - graphed_v_vect = always_redraw( - lambda: get_v_vect().shift( - axes.coords_to_point(get_t(), 0) - ) - ) - v_graph_template = axes.get_graph( - v_func, x_max=total_time, - ) - v_graph = VMobject() - v_graph.set_stroke(RED, 2) - v_graph.add_updater(lambda m: m.pointwise_become_partial( - v_graph_template, - 0, get_t() / total_time, - )) - - # Acceleration - def get_a_vect(): - return Vector( - axes.y_axis.unit_size * g * DOWN - ) - - a_vect = get_a_vect() - a_vect.add_updater(lambda a: a.move_to( - get_ball_point(), UP, - )) - a_brace = Brace(a_vect, RIGHT) - always(a_brace.next_to, a_vect, RIGHT, SMALL_BUFF) - d2y_dt2_label = TexMobject( - "d^2", "{y}", "\\over dt}", "(t)" - ) - d2y_dt2_label.scale(0.8) - d2y_dt2_label.set_color_by_tex( - "y", BLUE, - ) - y_ddot_label = TexMobject("\\ddot y", "(t)") - y_ddot_label.set_color_by_tex("\\ddot y", YELLOW) - for label in d2y_dt2_label, y_ddot_label: - label.add_updater(lambda m: m.next_to( - a_brace, RIGHT, SMALL_BUFF - )) - a_graph = axes.get_graph( - lambda t: -g, x_max=total_time, - ) - a_graph.set_stroke(YELLOW, 2) - - graphed_a_vect = get_a_vect() - graphed_a_vect.add_updater(lambda a: a.move_to( - axes.coords_to_point(get_t(), 0), UP, - )) - - self.set_variables_as_attrs( - t_tracker, - graph, - y_label, - traj, - h_line, - v_vect, - v_brace, - dy_dt_label, - y_dot_label, - ball, - graphed_v_vect, - v_graph, - a_vect, - a_brace, - d2y_dt2_label, - y_ddot_label, - a_graph, - graphed_a_vect, - ) - - def show_trajectory(self): - self.add( - self.h_line, - self.traj, - self.ball, - self.graph, - self.y_label, - ) - self.play_trajectory() - self.wait() - - self.add( - self.v_vect, - self.v_brace, - self.dy_dt_label, - self.ball, - self.graphed_v_vect, - self.v_graph, - ) - self.play_trajectory() - self.wait() - - self.add( - self.a_vect, - self.ball, - self.a_brace, - self.d2y_dt2_label, - self.a_graph, - self.graphed_a_vect, - ) - self.play_trajectory() - self.wait() - - self.play( - ReplacementTransform( - self.dy_dt_label, - self.y_dot_label, - ), - ShowCreationThenFadeAround( - self.y_dot_label, - ), - ) - self.play( - ReplacementTransform( - self.d2y_dt2_label, - self.y_ddot_label, - ), - ShowCreationThenFadeAround( - self.y_ddot_label, - ), - ) - - def show_equation(self): - y_ddot = self.y_ddot_label - new_y_ddot = y_ddot.deepcopy() - new_y_ddot.clear_updaters() - - equation = VGroup( - new_y_ddot, - *TexMobject( - "=", "-g", - tex_to_color_map={"-g": YELLOW}, - ), - ) - new_y_ddot.next_to(equation[1], LEFT, SMALL_BUFF) - equation.move_to(self.axes) - equation.to_edge(UP) - - self.play( - TransformFromCopy(y_ddot, new_y_ddot), - Write(equation[1:]), - FadeOut(self.graph), - FadeOut(self.y_label), - FadeOut(self.h_line), - FadeOut(self.v_graph), - FadeOut(self.graphed_v_vect), - FadeOut(self.graphed_a_vect), - ) - - self.equation = equation - - def solve_for_velocity(self): - axes = self.axes - equation = self.equation - v_graph = self.v_graph.deepcopy() - v_graph.clear_updaters() - v_start_point = v_graph.get_start() - origin = axes.coords_to_point(0, 0) - offset = v_start_point - origin - v_graph.shift(-offset) - - tex_question, answer1, answer2 = derivs = [ - TexMobject( - "{d", "(", *term, ")", "\\over", "dt}", "(t)", - "=", "-g", - tex_to_color_map={ - "-g": YELLOW, - "v_0": RED, - "?": RED, - } - ) - for term in [ - ("?", "?", "?", "?"), - ("-g", "t"), - ("-g", "t", "+", "v_0",), - ] - ] - for x in range(2): - answer1.submobjects.insert( - 4, VectorizedPoint(answer1[4].get_left()) - ) - for deriv in derivs: - deriv.next_to(equation, DOWN, MED_LARGE_BUFF) - - question = TextMobject( - "What function has slope $-g$?", - tex_to_color_map={"$-g$": YELLOW}, - ) - question.next_to(tex_question, DOWN) - question.set_stroke(BLACK, 5, background=True) - question.add_background_rectangle() - - v0_dot = Dot(v_start_point, color=PINK) - v0_label = TexMobject("v_0") - v0_label.set_color(RED) - v0_label.next_to(v0_dot, UR, buff=0) - - y_dot_equation = TexMobject( - "{\\dot y}", "(t)", "=", - "-g", "t", "+", "v_0", - tex_to_color_map={ - "{\\dot y}": RED, - "-g": YELLOW, - "v_0": RED, - } - ) - y_dot_equation.to_corner(UR) - - self.play( - FadeInFrom(tex_question, DOWN), - FadeInFrom(question, UP) - ) - self.wait() - self.add(v_graph, question) - self.play( - ReplacementTransform(tex_question, answer1), - ShowCreation(v_graph), - ) - self.wait() - self.play( - ReplacementTransform(answer1, answer2), - v_graph.shift, offset, - ) - self.play( - FadeInFromLarge(v0_dot), - FadeInFromDown(v0_label), - ) - self.wait() - self.play( - TransformFromCopy( - answer2[2:6], y_dot_equation[3:], - ), - Write(y_dot_equation[:3]), - equation.shift, LEFT, - ) - self.play( - FadeOut(question), - FadeOut(answer2), - ) - - self.remove(v_graph) - self.add(self.v_graph) - self.y_dot_equation = y_dot_equation - - def solve_for_position(self): - # Largely copied from above...not great - equation = self.equation - y_dot_equation = self.y_dot_equation - graph = self.graph - - all_terms = [ - ("?", "?", "?", "?"), - ("-", "(1/2)", "g", "t^2", "+", "v_0", "t"), - ("-", "(1/2)", "g", "t^2", "+", "v_0", "t", "+", "y_0"), - ] - tex_question, answer1, answer2 = derivs = [ - TexMobject( - "{d", "(", *term, ")", "\\over", "dt}", "(t)", - "=", - "-g", "t", "+", "v_0", - tex_to_color_map={ - "g": YELLOW, - "v_0": RED, - "?": BLUE, - "y_0": BLUE, - } - ) - for term in all_terms - ] - answer1.scale(0.8) - answer2.scale(0.8) - for deriv, terms in zip(derivs, all_terms): - for x in range(len(all_terms[-1]) - len(terms)): - n = 2 + len(terms) - deriv.submobjects.insert( - n, VectorizedPoint(deriv[n].get_left()) - ) - deriv.next_to( - VGroup(equation, y_dot_equation), - DOWN, MED_LARGE_BUFF + SMALL_BUFF - ) - deriv.shift_onto_screen() - deriv.add_background_rectangle_to_submobjects() - - y_equation = TexMobject( - "y", "(t)", "=", - "-", "(1/2)", "g", "t^2", - "+", "v_0", "t", - "+", "y_0", - tex_to_color_map={ - "y": BLUE, - "g": YELLOW, - "v_0": RED, - } - ) - y_equation.next_to( - VGroup(equation, y_dot_equation), - DOWN, MED_LARGE_BUFF, - ) - - self.play( - FadeInFrom(tex_question, DOWN), - ) - self.wait() - self.add(graph, tex_question) - self.play( - ReplacementTransform(tex_question, answer1), - ShowCreation(graph), - ) - self.add(graph, answer1) - self.wait() - self.play(ReplacementTransform(answer1, answer2)) - self.add(graph, answer2) - g_updaters = graph.updaters - graph.clear_updaters() - self.play( - graph.shift, 2 * DOWN, - rate_func=there_and_back, - run_time=2, - ) - graph.add_updater(g_updaters[0]) - self.wait() - br = BackgroundRectangle(y_equation) - self.play( - FadeIn(br), - ReplacementTransform( - answer2[2:11], - y_equation[3:] - ), - FadeIn(y_equation[:3]), - FadeOut(answer2[:2]), - FadeOut(answer2[11:]), - ) - self.play(ShowCreationThenFadeAround(y_equation)) - self.play_trajectory() - - # - def play_trajectory(self, *added_anims, **kwargs): - self.t_tracker.set_value(0) - self.play( - ApplyMethod( - self.t_tracker.set_value, 5, - rate_func=linear, - run_time=self.total_time, - ), - *added_anims, - ) - self.wait() - - -class SimpleProjectileEquationVGraphFreedom(SimpleProjectileEquation): - def construct(self): - self.add_axes() - self.setup_trajectory() - self.clear() - v_graph = self.v_graph - self.t_tracker.set_value(5) - v_graph.update() - v_graph.clear_updaters() - self.add(v_graph) - self.play(v_graph.shift, 5 * DOWN, run_time=2) - self.play(v_graph.shift, 5 * UP, run_time=2) - - -class UniversalGravityLawSymbols(Scene): - def construct(self): - x1_tex = "\\vec{\\textbf{x}}_1" - x2_tex = "\\vec{\\textbf{x}}_2" - a1_tex = "\\vec{\\textbf{a}}_1" - new_brown = interpolate_color(LIGHT_GREY, LIGHT_BROWN, 0.5) - law = TexMobject( - "F_1", "=", "m_1", a1_tex, "=", - "G", "m_1", "m_2", - "\\left({", x2_tex, "-", x1_tex, "\\over", - "||", x2_tex, "-", x1_tex, "||", "}\\right)", - "\\left({", "1", "\\over", - "||", x2_tex, "-", x1_tex, "||^2", "}\\right)", - tex_to_color_map={ - x1_tex: BLUE_C, - "m_1": BLUE_C, - x2_tex: new_brown, - "m_2": new_brown, - a1_tex: YELLOW, - } - ) - law.to_edge(UP) - - force = law[:4] - constants = law[4:8] - unit_vect = law[8:19] - inverse_square = law[19:] - parts = VGroup( - force, unit_vect, inverse_square - ) - - words = VGroup( - TextMobject("Force on\\\\mass 1"), - TextMobject("Unit vector\\\\towards mass 2"), - TextMobject("Inverse square\\\\law"), - ) - - self.add(law) - - braces = VGroup() - rects = VGroup() - for part, word in zip(parts, words): - brace = Brace(part, DOWN) - word.scale(0.8) - word.next_to(brace, DOWN) - rect = SurroundingRectangle(part) - rect.set_stroke(YELLOW, 1) - braces.add(brace) - rects.add(rect) - - self.play( - ShowCreationThenFadeOut(rects[0]), - GrowFromCenter(braces[0]), - FadeInFrom(words[0], UP) - ) - self.wait() - self.play( - ShowCreationThenFadeOut(rects[1]), - GrowFromCenter(braces[1]), - FadeInFrom(words[1], UP) - ) - self.wait() - self.play( - ShowCreationThenFadeOut(rects[2]), - TransformFromCopy(*braces[1:3]), - FadeInFrom(words[2], UP), - ) - self.wait() - - # Position derivative - v1_tex = "\\vec{\\textbf{v}}_1" - kw = { - "tex_to_color_map": { - x1_tex: BLUE_C, - v1_tex: RED, - } - } - x_deriv = TexMobject( - "{d", x1_tex, "\\over", "dt}", "=", v1_tex, **kw - ) - x_deriv.to_corner(UL) - v_deriv = TexMobject( - "{d", v1_tex, "\\over", "dt}", "=", **kw - ) - - # Make way - law.generate_target() - lt = law.target - lt.to_edge(RIGHT) - lt[6].fade(1) - lt[:6].align_to(lt[6], RIGHT) - lt[:3].fade(1) - v_deriv.next_to(lt[3], LEFT) - - self.play( - FadeInFromDown(x_deriv), - MoveToTarget(law), - braces[1:].align_to, lt, RIGHT, - MaintainPositionRelativeTo(words[1:], braces[1:]), - FadeOut(words[0]), - FadeOut(braces[0]), - ) - self.play(ShowCreationThenFadeAround(x_deriv)) - - self.play( - TransformFromCopy( - x_deriv.get_part_by_tex(v1_tex), - v_deriv.get_part_by_tex(v1_tex), - ), - Write(VGroup(*filter( - lambda m: m is not v_deriv.get_part_by_tex(v1_tex), - v_deriv, - ))) - ) - - x_parts = law.get_parts_by_tex(x1_tex) - self.play( - TransformFromCopy( - x_deriv.get_parts_by_tex(x1_tex), - x_parts.copy(), - remover=True, - path_arc=30 * DEGREES, - ) - ) - self.play( - LaggedStartMap( - ShowCreationThenFadeAround, - x_parts - ) - ) - self.wait() - - -class ExampleTypicalODE(TeacherStudentsScene): - def construct(self): - examples = VGroup( - TexMobject( - "{\\dot x}(t) = k{x}(t)", - tex_to_color_map={ - "{\\dot x}": BLUE, - "{x}": BLUE, - }, - ), - get_ode(), - TexMobject( - "{\\partial T", "\\over", "\\partial t} = ", - "{\\partial^2 T", "\\over", "\\partial x^2}", "+", - "{\\partial^2 T", "\\over", "\\partial y^2}", "+", - "{\\partial^2 T", "\\over", "\\partial z^2}", - tex_to_color_map={ - "T": RED, - } - ), - ) - examples[1].get_parts_by_tex("theta").set_color(GREEN) - examples.arrange(DOWN, buff=MED_LARGE_BUFF) - examples.to_edge(UP) - - self.play( - FadeInFrom(examples[0], UP), - self.teacher.change, "raise_right_hand", - ) - self.play( - FadeInFrom(examples[1], UP), - self.get_student_changes( - *3 * ["pondering"], - look_at_arg=examples, - ), - ) - self.play( - FadeInFrom(examples[2], UP) - ) - self.wait(5) - - -class ShowDerivativeVideo(Scene): - def construct(self): - 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( - fill_opacity=1, - fill_color=BLACK, - stroke_width=0, - ) - rect.next_to(title, DOWN) - animated_rect = AnimatedBoundary(rect) - - self.add(title, rect) - self.add(animated_rect) - self.wait(5) - self.play(ReplacementTransform(title, title2)) - self.wait(10) - - -class SubtleAirCurrents(Scene): - def construct(self): - pass - - -class DefineODE(Scene): - CONFIG = { - "pendulum_config": { - "length": 2, - "top_point": 5 * RIGHT + 2 * UP, - "initial_theta": 150 * DEGREES, - "mu": 0.3, - }, - "axes_config": { - "y_axis_config": {"unit_size": 0.75}, - "y_max": PI, - "y_min": -PI, - "x_max": 10, - "x_axis_config": { - "numbers_to_show": range(2, 10, 2), - "unit_size": 1, - } - }, - } - - def construct(self): - self.add_graph() - self.write_differential_equation() - self.dont_know_the_value() - self.show_value_slope_curvature() - self.write_ode() - self.show_second_order() - self.show_higher_order_examples() - self.show_changing_curvature_group() - - def add_graph(self): - pendulum = Pendulum(**self.pendulum_config) - axes = ThetaVsTAxes(**self.axes_config) - - axes.center() - axes.to_corner(DL) - graph = axes.get_live_drawn_graph(pendulum) - - pendulum.start_swinging() - self.add(axes, pendulum, graph) - - self.pendulum = pendulum - self.axes = axes - self.graph = graph - - def write_differential_equation(self): - de_word = TextMobject("Differential", "Equation") - de_word.to_edge(UP, buff=MED_SMALL_BUFF) - - equation = get_ode() - equation.next_to(de_word, DOWN) - thetas = equation.get_parts_by_tex("\\theta") - - lines = VGroup(*[ - Line(v, 1.2 * v) - for v in compass_directions(25) - ]) - lines.replace(equation, stretch=True) - lines.scale(1.5) - lines.set_stroke(YELLOW) - lines.shuffle() - - self.add(equation) - self.wait(5) - self.play( - ShowPassingFlashWithThinningStrokeWidth( - lines, - lag_ratio=0.002, - run_time=1.5, - time_width=0.9, - n_segments=5, - ) - ) - self.play(FadeInFromDown(de_word)) - self.wait(2) - self.play( - LaggedStartMap( - ApplyMethod, thetas, - lambda m: (m.shift, 0.25 * DOWN), - rate_func=there_and_back, - ) - ) - self.wait() - - self.de_word = de_word - self.equation = equation - - def dont_know_the_value(self): - graph = self.graph - pendulum = self.pendulum - - q_marks = VGroup(*[ - TexMobject("?").move_to(graph.point_from_proportion(a)) - for a in np.linspace(0, 1, 20) - ]) - q_marks.set_stroke(width=0, background=True) - self.play( - VFadeOut(graph), - FadeOut(pendulum), - LaggedStart(*[ - UpdateFromAlphaFunc( - q_mark, - lambda m, a: m.set_height(0.5 * (1 + a)).set_fill( - opacity=there_and_back(a) - ), - ) - for q_mark in q_marks - ], lag_ratio=0.01, run_time=2) - ) - self.remove(q_marks) - - def show_value_slope_curvature(self): - axes = self.axes - p = self.pendulum - graph = axes.get_graph( - lambda t: p.initial_theta * np.cos( - np.sqrt(p.gravity / p.length) * t - ) * np.exp(-p.mu * t / 2) - ) - - tex_config = { - "tex_to_color_map": { - "{\\theta}": BLUE, - "{\\dot\\theta}": RED, - "{\\ddot\\theta}": YELLOW, - }, - "height": 0.5, - } - theta, d_theta, dd_theta = [ - TexMobject( - "{" + s + "\\theta}(t)", - **tex_config - ) - for s in ("", "\\dot", "\\ddot") - ] - - t_tracker = ValueTracker(2.5) - 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=RED, - 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(YELLOW, 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) - - theta.next_to(v_line, RIGHT, SMALL_BUFF) - d_theta.next_to(slope_line.get_end(), UP, SMALL_BUFF) - dd_theta.next_to(curve.get_end(), RIGHT, SMALL_BUFF) - thetas = VGroup(theta, d_theta, dd_theta) - - words = VGroup( - TextMobject("= Height").set_color(BLUE), - TextMobject("= Slope").set_color(RED), - TextMobject("= ``Curvature''").set_color(YELLOW), - ) - words.scale(0.75) - for word, sym in zip(words, thetas): - word.next_to(sym, RIGHT, buff=2 * SMALL_BUFF) - sym.word = word - - self.play( - ShowCreation(v_line), - FadeInFromPoint(dot, v_line.get_start()), - FadeInFrom(theta, DOWN), - FadeInFrom(theta.word, DOWN), - ) - self.add(slope_line, dot) - self.play( - ShowCreation(slope_line), - FadeInFrom(d_theta, LEFT), - FadeInFrom(d_theta.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_theta, LEFT), - FadeInFrom(dd_theta.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(thetas, words) - self.fake_graph = graph - - def write_ode(self): - equation = self.equation - axes = self.axes - de_word = self.de_word - - ts = equation.get_parts_by_tex("{t}") - t_rects = VGroup(*map(SurroundingRectangle, ts)) # Rawr - x_axis = axes.x_axis - x_axis_line = Line( - x_axis.get_start(), x_axis.get_end(), - stroke_color=YELLOW, - stroke_width=5, - ) - - ordinary = TextMobject("Ordinary") - de_word.generate_target() - group = VGroup(ordinary, de_word.target) - group.arrange(RIGHT) - group.to_edge(UP) - ordinary_underline = Line(LEFT, RIGHT) - ordinary_underline.replace(ordinary, dim_to_match=0) - ordinary_underline.next_to(ordinary, DOWN, SMALL_BUFF) - ordinary_underline.set_color(YELLOW) - - self.play( - ShowCreationThenFadeOut( - t_rects, - lag_ratio=0.8 - ), - ShowCreationThenFadeOut(x_axis_line) - ) - self.play( - MoveToTarget(de_word), - FadeInFrom(ordinary, RIGHT), - GrowFromCenter(ordinary_underline) - ) - self.play(FadeOut(ordinary_underline)) - self.wait() - - self.remove(ordinary, de_word) - ode_word = self.ode_word = VGroup(*ordinary, *de_word) - ode_initials = VGroup(*[word[0] for word in ode_word]) - ode_initials.generate_target() - ode_initials.target.scale(1.2) - ode_initials.target.set_color(PINK) - ode_initials.target.arrange( - RIGHT, buff=0.5 * SMALL_BUFF, aligned_edge=DOWN - ) - ode_initials.target.to_edge(UP, buff=MED_SMALL_BUFF) - - ode_remaining_letters = VGroup(*it.chain(*[ - word[1:] for word in ode_word - ])) - ode_remaining_letters.generate_target() - for mob in ode_remaining_letters.target: - mob.shift(0.2 * UP) - mob.fade(1) - - self.play( - MoveToTarget(ode_initials), - MoveToTarget(ode_remaining_letters, lag_ratio=0.05), - ) - self.wait() - - self.ode_initials = ode_initials - - def show_second_order(self): - so = TextMobject("Second order") - so.scale(1.4) - ode = self.ode_initials - ode.generate_target() - group = VGroup(so, ode.target) - group.arrange(RIGHT, aligned_edge=DOWN) - group.to_edge(UP, buff=MED_SMALL_BUFF) - - second_deriv = self.equation[:5] - - self.play( - Write(so), - MoveToTarget(ode), - ) - self.wait() - self.play(FocusOn(second_deriv)) - self.play( - Indicate(second_deriv, color=YELLOW), - ) - self.wait() - - self.second_order_word = so - - def show_higher_order_examples(self): - main_example = self.get_main_example() - tex_config = {"tex_to_color_map": {"{x}": BLUE}} - example3 = VGroup( - TextMobject("Third order ODE"), - TexMobject( - "\\dddot {x}(t) + \\dot {x}(t)^2 = 0", - **tex_config, - ) - ) - example4 = VGroup( - TextMobject("Fourth order ODE"), - TexMobject( - "\\ddddot {x}(t) +", - "a\\dddot {x}(t) \\dot {x}(t) + ", - "b \\ddot {x}(t) {x}(t)", - "= 1", - **tex_config, - ) - ) - for example in [example3, example4]: - example[0].scale(1.2) - example.arrange(DOWN, buff=MED_LARGE_BUFF) - example.to_edge(UP, buff=MED_SMALL_BUFF) - - self.play( - FadeOut(main_example), - FadeIn(example3), - ) - self.wait(2) - self.play( - FadeOut(example3), - FadeIn(example4), - ) - self.wait(2) - self.play( - FadeOut(example4), - FadeIn(main_example), - ) - 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 - labels = self.curvature_group_labels - graph = VMobject() - graph.pointwise_become_partial( - self.fake_graph, - t_tracker.get_value() / self.axes.x_max, - 1, - ) - dashed_graph = DashedVMobject(graph, num_dashes=100) - dashed_graph.set_stroke(GREEN, 1) - - self.play(FadeOut(labels)) - self.add(dashed_graph, curvature_group) - self.play( - t_tracker.set_value, 10, - ShowCreation(dashed_graph), - run_time=15, - rate_func=linear, - ) - self.wait() - - -# Largely a copy of DefineODE, which is not great. -# But what can you do? -class SecondOrderEquationExample(DefineODE): - def construct(self): - 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}": RED, - "{\\ddot x}": YELLOW, - } - ) - 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}": RED, - "{\\ddot x}": YELLOW, - }, - "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=RED, - 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(YELLOW, 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(RED), - TextMobject("= ``Curvature''").set_color(YELLOW), - ) - 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): - 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): - def construct(self): - ode = TexMobject( - "{\\ddot\\theta}", "(t)", "=", - "-\\mu", "{\\dot\\theta}", "(t)" - "-(g / L)\\sin\\big(", "{\\theta}", "(t)\\big)", - tex_to_color_map={ - "{\\ddot\\theta}": RED, - "{\\dot\\theta}": YELLOW, - "{\\theta}": BLUE, - # "{t}": WHITE, - } - ) - so_word = TextMobject("Second order ODE") - sys_word = TextMobject("System of two first order ODEs") - - system1 = self.get_system("{\\theta}", "{\\dot\\theta}") - system2 = self.get_system("{\\theta}", "{\\omega}") - - so_word.to_edge(UP) - ode.next_to(so_word, DOWN) - sys_word.move_to(ORIGIN) - 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() - - self.play( - TransformFromCopy( - ode[3:], system1[3].get_entries()[1], - ), - TransformFromCopy(ode[2], system1[2]), - TransformFromCopy( - ode[:2], VGroup( - system1[0], - system1[1].get_entries()[1], - ) - ), - ) - self.play( - FadeIn(system1[1].get_brackets()), - FadeIn(system1[1].get_entries()[0]), - FadeIn(system1[3].get_brackets()), - FadeIn(system1[3].get_entries()[0]), - ) - self.play( - FadeInFromDown(sys_word) - ) - self.wait() - self.play(LaggedStartMap( - ShowCreationThenFadeAround, - theta_dots, - surrounding_rectangle_config={ - "color": PINK, - } - )) - - self.play(ReplacementTransform(system1, system2)) - self.wait() - - def get_system(self, tex1, tex2): - system = VGroup( - TexMobject("d \\over dt"), - self.get_vector_symbol( - tex1 + "(t)", - tex2 + "(t)", - ), - TexMobject("="), - self.get_vector_symbol( - tex2 + "(t)", - "".join([ - "-\\mu", tex2, "(t)", - "-(g / L) \\sin\\big(", - tex1, "(t)", "\\big)", - ]) - ) - ) - system.arrange(RIGHT) - return system - - -class FromODEToVectorField(Scene): - def construct(self): - matrix_config = { - "bracket_v_buff": 2 * SMALL_BUFF, - "element_to_mobject_config": { - "tex_to_color_map": { - "x": GREEN, - "y": RED, - "z": BLUE, - }, - } - } - vect = get_vector_symbol( - "x(t)", "y(t)", "z(t)", - **matrix_config, - ) - d_vect = get_vector_symbol( - "\\sigma\\big(y(t) - x(t)\\big)", - "x(t)\\big(\\rho - z(t)\\big) - y(t)", - "x(t)y(t) - \\beta z(t)", - **matrix_config - ) - equation = VGroup( - TexMobject("d \\over dt").scale(1.5), - vect, - TexMobject("="), - d_vect - ) - equation.scale(0.8) - equation.arrange(RIGHT) - equation.to_edge(UP) - - arrow = Vector(DOWN, color=YELLOW) - arrow.next_to(equation, DOWN) - - self.add(equation) - self.play(ShowCreation(arrow)) - self.wait() - - -class LorenzVectorField(ExternallyAnimatedScene): - pass - - -class ThreeBodiesInSpace(SpecialThreeDScene): - CONFIG = { - "masses": [1, 6, 3], - "colors": [RED_E, GREEN_E, BLUE_E], - "G": 0.5, - "play_time": 60, - } - - def construct(self): - self.add_axes() - self.add_bodies() - self.add_trajectories() - self.let_play() - - def add_axes(self): - axes = self.axes = self.get_axes() - 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 - - bodies = self.bodies = VGroup() - velocity_vectors = VGroup() - - centers = self.get_initial_positions() - - for mass, color, center in zip(masses, colors, centers): - body = self.get_sphere( - checkerboard_colors=[ - color, color - ], - color=color, - stroke_width=0.1, - ) - body.set_opacity(0.75) - body.mass = mass - body.radius = 0.08 * np.sqrt(mass) - body.set_width(2 * body.radius) - - body.point = center - body.move_to(center) - - body.velocity = self.get_initial_velocity( - center, centers, mass - ) - - vect = self.get_velocity_vector_mob(body) - - bodies.add(body) - velocity_vectors.add(vect) - - total_mass = np.sum([body.mass for body in bodies]) - center_of_mass = reduce(op.add, [ - body.mass * body.get_center() / total_mass - for body in bodies - ]) - average_momentum = reduce(op.add, [ - body.mass * body.velocity / total_mass - for body in bodies - ]) - for body in bodies: - body.shift(-center_of_mass) - body.velocity -= average_momentum - - def get_initial_positions(self): - return [ - np.dot( - 4 * (np.random.random(3) - 0.5), - [RIGHT, UP, OUT] - ) - for x in range(len(self.masses)) - ] - - def get_initial_velocity(self, center, centers, mass): - to_others = [ - center - center2 - for center2 in centers - ] - velocity = 0.2 * mass * normalize(np.cross(*filter( - lambda diff: get_norm(diff) > 0, - to_others - ))) - return velocity - - def add_trajectories(self): - def update_trajectory(traj, dt): - new_point = traj.body.point - if get_norm(new_point - traj.points[-1]) > 0.01: - traj.add_smooth_curve_to(new_point) - - for body in self.bodies: - traj = VMobject() - traj.body = body - traj.start_new_path(body.point) - traj.set_stroke(body.color, 1, opacity=0.75) - traj.add_updater(update_trajectory) - self.add(traj, body) - - def let_play(self): - bodies = self.bodies - bodies.add_updater(self.update_bodies) - # Break it up to see partial files as - # it's rendered - self.add(bodies) - for x in range(int(self.play_time)): - self.wait() - - # - def get_velocity_vector_mob(self, body): - def draw_vector(): - center = body.get_center() - vect = Arrow( - center, - center + body.velocity, - buff=0, - color=RED, - ) - vect.set_shade_in_3d(True) - return vect - # length = vect.get_length() - # if length > 2: - # vect.scale( - # 2 / length, - # about_point=vect.get_start(), - # ) - return always_redraw(draw_vector) - - def update_bodies(self, bodies, dt): - G = self.G - - num_mid_steps = 1000 - for x in range(num_mid_steps): - for body in bodies: - acceleration = np.zeros(3) - for body2 in bodies: - if body2 is body: - continue - diff = body2.point - body.point - m2 = body2.mass - R = get_norm(diff) - acceleration += G * m2 * diff / (R**3) - body.point += body.velocity * dt / num_mid_steps - body.velocity += acceleration * dt / num_mid_steps - for body in bodies: - body.move_to(body.point) - return bodies - - -class AltThreeBodiesInSpace(ThreeBodiesInSpace): - CONFIG = { - "random_seed": 6, - "masses": [1, 2, 6], - } - - -class TwoBodiesInSpace(ThreeBodiesInSpace): - CONFIG = { - "colors": [GREY, BLUE], - "masses": [6, 36], - "play_time": 60, - } - - def construct(self): - self.add_axes() - self.add_bodies() - self.add_trajectories() - self.add_velocity_vectors() - self.add_force_vectors() - self.let_play() - - def add_bodies(self): - super().add_bodies() - for body in self.bodies: - body.point = 3 * normalize(body.get_center()) - # body.point += 2 * IN - # body.velocity += (4 / 60) * OUT - body.move_to(body.point) - - def get_initial_positions(self): - return [ - np.dot( - 6 * (np.random.random(3) - 0.5), - [RIGHT, UP, ORIGIN] - ) - for x in range(len(self.masses)) - ] - - def get_initial_velocity(self, center, centers, mass): - return 0.75 * normalize(np.cross(center, OUT)) - - def add_velocity_vectors(self): - vectors = VGroup(*[ - self.get_velocity_vector(body) - for body in self.bodies - ]) - self.velocity_vectors = vectors - self.add(vectors) - - def get_velocity_vector(self, body): - def create_vector(b): - v = Vector( - b.velocity, - color=RED, - max_stroke_width_to_length_ratio=3, - ) - v.set_stroke(width=3) - v.shift( - b.point + b.radius * normalize(b.velocity) - - v.get_start(), - ) - v.set_shade_in_3d(True) - return v - return always_redraw(lambda: create_vector(body)) - - def add_force_vectors(self): - vectors = VGroup(*[ - self.get_force_vector(b1, b2) - for (b1, b2) in (self.bodies, self.bodies[::-1]) - ]) - self.force_vectors = vectors - self.add(vectors) - - def get_force_vector(self, body1, body2): - def create_vector(b1, b2): - r = b2.point - b1.point - F = r / (get_norm(r)**3) - v = Vector( - 4 * F, - color=YELLOW, - max_stroke_width_to_length_ratio=3, - ) - v.set_stroke(width=3) - v.shift( - b1.point + b1.radius * normalize(F) - - v.get_start(), - ) - v.set_shade_in_3d(True) - return v - return always_redraw(lambda: create_vector(body1, body2)) - - -class TwoBodiesWithZPart(TwoBodiesInSpace): - def add_bodies(self): - super().add_bodies() - for body in self.bodies: - body.point += 3 * IN - body.velocity += (6 / 60) * OUT - - -class LoveExample(PiCreatureScene): - def construct(self): - self.show_hearts() - self.add_love_trackers() - self.break_down_your_rule() - self.break_down_their_rule() - - def create_pi_creatures(self): - you = You() - you.shift(FRAME_WIDTH * LEFT / 4) - you.to_edge(DOWN) - - tau = TauCreature(color=GREEN) - tau.flip() - tau.shift(FRAME_WIDTH * RIGHT / 4) - tau.to_edge(DOWN) - - self.you = you - self.tau = tau - return (you, tau) - - def show_hearts(self): - you, tau = self.you, self.tau - hearts = VGroup() - n_hearts = 20 - for x in range(n_hearts): - heart = SuitSymbol("hearts") - heart.scale(0.5 + 2 * np.random.random()) - heart.shift(np.random.random() * 4 * RIGHT) - heart.shift(np.random.random() * 4 * UP) - hearts.add(heart) - hearts.move_to(2 * DOWN) - hearts.add_updater(lambda m, dt: m.shift(2 * dt * UP)) - - self.add(hearts) - self.play( - LaggedStartMap( - UpdateFromAlphaFunc, hearts, - lambda heart: ( - heart, - lambda h, a: h.set_opacity( - there_and_back(a) - ).shift(0.02 * UP) - ), - lag_ratio=0.01, - run_time=3, - suspend_mobject_updating=False, - ), - ApplyMethod( - you.change, 'hooray', tau.eyes, - run_time=2, - rate_func=squish_rate_func(smooth, 0.5, 1) - ), - ApplyMethod( - tau.change, 'hooray', you.eyes, - run_time=2, - rate_func=squish_rate_func(smooth, 0.5, 1) - ), - ) - self.remove(hearts) - self.wait() - - def add_love_trackers(self): - self.init_ps_point() - self.add_love_decimals() - self.add_love_number_lines() - self.tie_creature_state_to_ps_point() - - self.play(Rotating( - self.ps_point, - radians=-7 * TAU / 8, - about_point=ORIGIN, - run_time=10, - rate_func=linear, - )) - self.wait() - - def break_down_your_rule(self): - label1 = self.love_1_label - label2 = self.love_2_label - ps_point = self.ps_point - - up_arrow = Vector(UP, color=GREEN) - down_arrow = Vector(DOWN, color=RED) - for arrow in (up_arrow, down_arrow): - arrow.next_to(label1, RIGHT) - - self.play(GrowArrow(up_arrow)) - self.play( - self.tau.love_eyes.scale, 1.25, - self.tau.love_eyes.set_color, BLUE_C, - rate_func=there_and_back, - ) - self.play( - ps_point.shift, 6 * RIGHT, - run_time=2, - ) - self.wait() - ps_point.shift(13 * DOWN) - self.play( - FadeOut(up_arrow), - GrowArrow(down_arrow), - ) - self.play( - ps_point.shift, 11 * LEFT, - run_time=3, - ) - self.wait() - - # Derivative - equation = get_love_equation1() - equation.shift(0.5 * UP) - deriv, equals, a, h2 = equation - - self.play( - Write(deriv[:-1]), - Write(equals), - Write(a), - TransformFromCopy(label1[0], deriv.heart), - TransformFromCopy(label2[0], h2), - ) - self.wait() - self.play( - equation.scale, 0.5, - equation.to_corner, UL, - FadeOut(down_arrow) - ) - - def break_down_their_rule(self): - label1 = self.love_1_label - label2 = self.love_2_label - ps_point = self.ps_point - - up_arrow = Vector(UP, color=GREEN) - down_arrow = Vector(DOWN, color=RED) - for arrow in (up_arrow, down_arrow): - arrow.next_to(label2, RIGHT) - - # Derivative - equation = get_love_equation2() - equation.shift(0.5 * UP) - deriv, equals, mb, h1 = equation - - self.play( - Write(deriv[:-1]), - Write(equals), - Write(mb), - TransformFromCopy(label1[0], h1), - TransformFromCopy(label2[0], deriv.heart), - ) - - self.play(GrowArrow(up_arrow)) - self.play( - ps_point.shift, 13 * UP, - run_time=3, - ) - self.wait() - self.play( - ps_point.shift, 11 * RIGHT, - ) - self.play( - FadeOut(up_arrow), - GrowArrow(down_arrow), - ) - self.play( - ps_point.shift, 13 * DOWN, - run_time=3, - ) - self.wait() - - # - def init_ps_point(self): - self.ps_point = VectorizedPoint(np.array([5.0, 5.0, 0])) - - def get_love1(self): - return self.ps_point.get_location()[0] - - def get_love2(self): - return self.ps_point.get_location()[1] - - def set_loves(self, love1=None, love2=None): - if love1 is not None: - self.ps_point.set_x(love1) - if love2 is not None: - self.ps_point.set_x(love2) - - def add_love_decimals(self): - self.love_1_label = self.add_love_decimal( - 1, self.get_love1, self.you.get_color(), -3, - ) - self.love_2_label = self.add_love_decimal( - 2, self.get_love2, self.tau.get_color(), 3, - ) - - def add_love_decimal(self, index, value_func, color, x_coord): - d = DecimalNumber(include_sign=True) - d.add_updater(lambda d: d.set_value(value_func())) - - label = get_heart_var(index) - label.move_to(x_coord * RIGHT) - label.to_edge(UP) - eq = TexMobject("=") - eq.next_to(label, RIGHT, SMALL_BUFF) - eq.shift(SMALL_BUFF * UP) - d.next_to(eq, RIGHT, SMALL_BUFF) - - self.add(label, eq, d) - return VGroup(label, eq, d) - - def add_love_number_lines(self): - nl1 = NumberLine( - x_min=-8, - x_max=8, - unit_size=0.25, - tick_frequency=2, - number_scale_val=0.25, - ) - nl1.set_stroke(width=1) - nl1.next_to(self.love_1_label, DOWN) - nl1.add_numbers(*range(-6, 8, 2)) - - nl2 = nl1.copy() - nl2.next_to(self.love_2_label, DOWN) - - dot1 = Dot(color=self.you.get_color()) - dot1.add_updater(lambda d: d.move_to( - nl1.number_to_point(self.get_love1()) - )) - dot2 = Dot(color=self.tau.get_color()) - dot2.add_updater(lambda d: d.move_to( - nl2.number_to_point(self.get_love2()) - )) - - self.add(nl1, nl2, dot1, dot2) - - def get_love_eyes(self, eyes): - hearts = VGroup() - for eye in eyes: - heart = SuitSymbol("hearts") - heart.match_width(eye) - heart.move_to(eye) - heart.scale(1.25) - heart.set_stroke(BLACK, 1) - hearts.add(heart) - hearts.add_updater( - lambda m: m.move_to(eyes) - ) - return hearts - - def tie_creature_state_to_ps_point(self): - # Quite a mess, but I'm coding in a rush here... - you = self.you - you_copy = you.copy() - tau = self.tau - tau_copy = tau.copy() - - you.love_eyes = self.get_love_eyes(you.eyes) - tau.love_eyes = self.get_love_eyes(tau.eyes) - - self.add(you.love_eyes) - self.add(tau.love_eyes) - - you_height = you.get_height() - tau_height = tau.get_height() - - you_bottom = you.get_bottom() - tau_bottom = tau.get_bottom() - - def update_you(y): - love = self.get_love1() - - cutoff_values = [ - -5, -3, -1, 1, 3, 5 - ] - modes = [ - "angry", "sassy", "hesitant", - "plain", - "happy", "hooray", "surprised", - ] - - if love < cutoff_values[0]: - y.change(modes[0]) - elif love >= cutoff_values[-1]: - y.change(modes[-1]) - else: - i = 0 - while cutoff_values[i] < love: - i += 1 - m1 = modes[i - 1] - m2 = modes[i] - y.change(m1) - you_copy.change(m2) - for mob in y, you_copy: - mob.set_height(you_height) - mob.move_to(you_bottom, DOWN) - - alpha = inverse_interpolate( - cutoff_values[i - 1], - cutoff_values[i], - love, - ) - s_alpha = squish_rate_func(smooth, 0.25, 1)(alpha) - if s_alpha > 0: - y.align_data(you_copy) - f1 = y.family_members_with_points() - f2 = you_copy.family_members_with_points() - for sm1, sm2 in zip(f1, f2): - sm1.interpolate(sm1, sm2, s_alpha) - y.look_at(tau.eyes) - if love < -4: - y.look_at(LEFT_SIDE) - # y.move_to( - # you_bottom + 0.025 * love * RIGHT, DOWN, - # ) - - l_alpha = np.clip( - inverse_interpolate(5, 5.5, love), - 0, 1 - ) - y.eyes.set_opacity(1 - l_alpha) - y.love_eyes.set_opacity(l_alpha) - - return y - - def update_tau(t): - love = self.get_love2() - - cutoff_values = [ - -5, -1.7, 1.7, 5 - ] - modes = [ - "angry", "confused", "plain", - "hooray", "hooray" - ] - - if love < cutoff_values[0]: - t.change(modes[0]) - elif love >= cutoff_values[-1]: - t.change(modes[-1]) - else: - i = 0 - while cutoff_values[i] < love: - i += 1 - m1 = modes[i - 1] - m2 = modes[i] - t.change(m1) - tau_copy.change(m2) - for mob in t, tau_copy: - mob.set_height(tau_height) - mob.move_to(tau_bottom, DOWN) - - alpha = inverse_interpolate( - cutoff_values[i - 1], - cutoff_values[i], - love, - ) - s_alpha = squish_rate_func(smooth, 0.25, 1)(alpha) - if s_alpha > 0: - t.align_data(tau_copy) - f1 = t.family_members_with_points() - f2 = tau_copy.family_members_with_points() - for sm1, sm2 in zip(f1, f2): - sm1.interpolate(sm1, sm2, s_alpha) - # t.move_to( - # tau_bottom + 0.025 * love * LEFT, DOWN, - # ) - t.look_at(you.eyes) - if love < -4: - t.look_at(RIGHT_SIDE) - - l_alpha = np.clip( - inverse_interpolate(5, 5.5, love), - 0, 1 - ) - t.eyes.set_opacity(1 - l_alpha) - t.love_eyes.set_opacity(l_alpha) - - you.add_updater(update_you) - tau.add_updater(update_tau) - - self.pi_creatures = VGroup() - - -class ComparePhysicsToLove(Scene): - def construct(self): - ode = get_ode() - ode.to_edge(UP) - thetas = ode.get_parts_by_tex("theta") - - love = VGroup( - get_love_equation1(), - get_love_equation2(), - ) - love.scale(0.5) - love.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) - love.move_to(DOWN) - hearts = VGroup(*filter( - lambda sm: isinstance(sm, SuitSymbol), - love.get_family() - )) - - arrow = DoubleArrow(love.get_top(), ode.get_bottom()) - - self.play(FadeInFrom(ode, DOWN)) - self.play(FadeInFrom(love, UP)) - self.wait() - self.play(LaggedStartMap( - ShowCreationThenFadeAround, - thetas, - )) - self.play(LaggedStartMap( - ShowCreationThenFadeAround, - hearts, - )) - self.wait() - self.play(ShowCreation(arrow)) - self.wait() - - -class FramesComparingPhysicsToLove(Scene): - CONFIG = { - "camera_config": {"background_color": DARKER_GREY} - } - - def construct(self): - ode = get_ode() - ode.to_edge(UP) - - love = VGroup( - get_love_equation1(), - get_love_equation2(), - ) - love.scale(0.5) - love.arrange(DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT) - - frames = VGroup(*[ - ScreenRectangle( - height=3.5, - fill_color=BLACK, - fill_opacity=1, - stroke_width=0, - ) - for x in range(2) - ]) - frames.arrange(RIGHT, buff=LARGE_BUFF) - frames.shift(DOWN) - - animated_frames = AnimatedBoundary(frames) - - ode.next_to(frames[0], UP) - love.next_to(frames[1], UP) - - self.add(frames, animated_frames) - self.add(ode, love) - - self.wait(15) - - -class PassageOfTime(Scene): - def construct(self): - clock = Clock() - clock[0].set_color(BLUE) - clock.set_stroke(width=1) - clock.scale(0.8) - clock.to_corner(UL) - passage = ClockPassesTime( - clock, - hours_passed=48, - ) - self.play(passage, run_time=10) - - -class WriteODESolvingCode(ExternallyAnimatedScene): - pass - - -class InaccurateComputation(Scene): - def construct(self): - h_line = DashedLine(LEFT_SIDE, RIGHT_SIDE) - h_line.to_edge(UP, buff=1.5) - words = VGroup( - TextMobject("Real number"), - TextMobject("IEEE 754\\\\representation"), - TextMobject("Error"), - ) - for i, word in zip([-1, 0, 1], words): - word.next_to(h_line, UP) - word.shift(i * FRAME_WIDTH * RIGHT / 3) - - lines = VGroup(*[ - DashedLine(TOP, BOTTOM) - for x in range(4) - ]) - lines.arrange(RIGHT) - lines.stretch_to_fit_width(FRAME_WIDTH) - - self.add(h_line, lines[1:-1], words) - - numbers = VGroup( - TexMobject("\\pi").scale(2), - TexMobject("e^{\\sqrt{163}\\pi}").scale(1.5), - ) - numbers.set_color(YELLOW) - numbers.set_stroke(width=0, background=True) - - bit_strings = VGroup( - TexMobject( - "01000000", - "01001001", - "00001111", - "11011011", - ), - TexMobject( - "01011100", - "01101001", - "00101110", - "00011001", - ) - ) - for mob in bit_strings: - mob.arrange(DOWN, buff=SMALL_BUFF) - for word in mob: - for submob, bit in zip(word, word.get_tex_string()): - if bit == "0": - submob.set_color(LIGHT_GREY) - errors = VGroup( - TexMobject( - "\\approx 8.7422 \\times 10^{-8}" - ), - TexMobject( - "\\approx 5{,}289{,}803{,}032.00", - ), - ) - errors.set_color(RED) - - content = VGroup(numbers, bit_strings, errors) - - for group, word in zip(content, words): - group[1].shift(3 * DOWN) - group.move_to(DOWN) - group.match_x(word) - - self.play(*map(Write, numbers)) - self.wait() - self.play( - TransformFromCopy(numbers, bit_strings), - lag_ratio=0.01, - run_time=2, - ) - self.wait() - self.play(FadeInFrom(errors, 3 * LEFT)) - self.wait() - - -class NewSceneName(Scene): - def construct(self): - pass diff --git a/active_projects/ode/part1/wordy_scenes.py b/active_projects/ode/part1/wordy_scenes.py deleted file mode 100644 index 91dc0ff1..00000000 --- a/active_projects/ode/part1/wordy_scenes.py +++ /dev/null @@ -1,855 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part1.shared_constructs import * - - -class SmallAngleApproximationTex(Scene): - def construct(self): - 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 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" - 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] - ) - 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) - quote.author_part.shift(RIGHT + 0.5 * DOWN) - quote.author_part.scale(1.2, about_edge=UL) - - 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(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( - FadeInFrom(c_word, UL), - GrowArrow(arrows[2]), - FadeInFrom(computation, UP) - ) - self.wait() - self.play( - 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): - ode = get_ode() - ode.to_edge(UP) - q1 = TextMobject("Find an exact solution") - q1.set_color(YELLOW) - q2 = TexMobject( - "\\text{What is }", "\\theta", "(t)", - "\\text{'s personality?}", - tex_to_color_map={"\\theta": BLUE}, - arg_separator="", - ) - theta = q2.get_part_by_tex("\\theta") - - for q in q1, q2: - q.scale(1.5) - q.next_to(ode, DOWN, MED_LARGE_BUFF) - eyes = Eyes(theta, height=0.1) - - self.add(ode) - self.add(q1) - self.wait() - self.play( - q1.scale, 0.3, - q1.to_corner, UR, MED_SMALL_BUFF, - ) - self.play(FadeInFrom(q2, DOWN)) - self.play( - eyes.blink, - rate_func=lambda t: smooth(1 - t), - ) - self.play(eyes.look_at, q2.get_left()) - self.play(eyes.look_at, q2.get_right()) - self.play( - eyes.blink, - rate_func=squish_rate_func(there_and_back) - ) - self.wait() - self.play( - eyes.change_mode, "confused", - eyes.look_at, ode.get_left(), - ) - self.play( - eyes.blink, - rate_func=squish_rate_func(there_and_back) - ) - - -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() - self.introduce_coord_groups() - self.count_coords() - - def init_coord_groups(self): - kwargs = { - "bracket_v_buff": 2 * SMALL_BUFF - } - positions = VGroup(*[ - get_vector_symbol(*[ - "{}_{}".format(s, i) - for s in "xyz" - ], **kwargs) - for i in range(1, 4) - ]) - velocities = VGroup(*[ - get_vector_symbol(*[ - "p^{}_{}".format(s, i) - for s in "xyz" - ], **kwargs) - for i in range(1, 4) - ]) - groups = VGroup(positions, velocities) - colors = [GREEN, RED, BLUE] - for group in groups: - for matrix in group: - matrix.coords = matrix.get_entries() - for coord, color in zip(matrix.coords, colors): - coord.set_color(color) - group.arrange(RIGHT) - groups.arrange(DOWN, buff=LARGE_BUFF) - groups.to_edge(LEFT) - - self.coord_groups = groups - - def introduce_coord_groups(self): - groups = self.coord_groups - x_group, p_group = groups - x_word = TextMobject("Positions") - p_word = TextMobject("Momenta") - words = VGroup(x_word, p_word) - for word, group in zip(words, groups): - word.next_to(group, UP) - - rect_groups = VGroup() - for group in groups: - rect_group = VGroup(*[ - SurroundingRectangle( - VGroup(*[ - tm.coords[i] - for tm in group - ]), - color=WHITE, - stroke_width=2, - ) - for i in range(3) - ]) - rect_groups.add(rect_group) - - self.play( - *[ - LaggedStartMap( - FadeInFrom, group, - lambda m: (m, UP), - run_time=1, - ) - for group in groups - ], - *map(FadeInFromDown, words), - ) - for rect_group in rect_groups: - self.play( - ShowCreationThenFadeOut( - rect_group, - lag_ratio=0.5, - ) - ) - self.wait() - - def count_coords(self): - coord_copies = VGroup() - for group in self.coord_groups: - for tex_mob in group: - for coord in tex_mob.coords: - coord_copy = coord.copy() - coord_copy.set_stroke( - WHITE, 2, background=True - ) - coord_copies.add(coord_copy) - - count = Integer() - count_word = TextMobject("18", "degrees \\\\ of freedom")[1] - count_group = VGroup(count, count_word) - count_group.arrange( - RIGHT, - aligned_edge=DOWN, - ) - count_group.scale(1.5) - count_group.next_to( - self.coord_groups, RIGHT, - aligned_edge=DOWN, - ) - count.add_updater( - lambda m: m.set_value(len(coord_copies)) - ) - count.add_updater( - lambda m: m.next_to(count_word[0][0], LEFT, aligned_edge=DOWN) - ) - - self.add(count_group) - self.play( - # ChangeDecimalToValue(count, len(coord_copies)), - ShowIncreasingSubsets(coord_copies), - run_time=1.5, - 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", - ] - } diff --git a/active_projects/ode/part2/fourier_series.py b/active_projects/ode/part2/fourier_series.py deleted file mode 100644 index 6a85ebfc..00000000 --- a/active_projects/ode/part2/fourier_series.py +++ /dev/null @@ -1,898 +0,0 @@ -from manimlib.imports import * -# import scipy - - -class FourierCirclesScene(Scene): - CONFIG = { - "n_vectors": 10, - "big_radius": 2, - "colors": [ - BLUE_D, - BLUE_C, - BLUE_E, - GREY_BROWN, - ], - "circle_style": { - "stroke_width": 2, - }, - "vector_config": { - "buff": 0, - "max_tip_length_to_length_ratio": 0.35, - "tip_length": 0.15, - "max_stroke_width_to_length_ratio": 10, - "stroke_width": 2, - }, - "circle_config": { - "stroke_width": 1, - }, - "base_frequency": 1, - "slow_factor": 0.25, - "center_point": ORIGIN, - "parametric_function_step_size": 0.001, - } - - def setup(self): - self.slow_factor_tracker = ValueTracker( - self.slow_factor - ) - self.vector_clock = ValueTracker(0) - self.vector_clock.add_updater( - lambda m, dt: m.increment_value( - self.get_slow_factor() * dt - ) - ) - self.add(self.vector_clock) - - def get_slow_factor(self): - return self.slow_factor_tracker.get_value() - - def get_vector_time(self): - return self.vector_clock.get_value() - - # - def get_freqs(self): - n = self.n_vectors - 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_vectors)] - - def get_color_iterator(self): - return it.cycle(self.colors) - - def get_rotating_vectors(self, freqs=None, coefficients=None): - vectors = VGroup() - self.center_tracker = VectorizedPoint(self.center_point) - - if freqs is None: - freqs = self.get_freqs() - if coefficients is None: - coefficients = self.get_coefficients() - - last_vector = None - for freq, coefficient in zip(freqs, coefficients): - if last_vector: - center_func = last_vector.get_end - else: - center_func = self.center_tracker.get_location - vector = self.get_rotating_vector( - coefficient=coefficient, - freq=freq, - center_func=center_func, - ) - vectors.add(vector) - last_vector = vector - return vectors - - def get_rotating_vector(self, coefficient, freq, center_func): - vector = Vector(RIGHT, **self.vector_config) - vector.scale(abs(coefficient)) - if abs(coefficient) == 0: - phase = 0 - else: - phase = np.log(coefficient).imag - vector.rotate(phase, about_point=ORIGIN) - vector.freq = freq - vector.phase = phase - vector.coefficient = coefficient - vector.center_func = center_func - vector.add_updater(self.update_vector) - return vector - - def update_vector(self, vector, dt): - time = self.get_vector_time() - vector.set_angle( - vector.phase + time * vector.freq * TAU - ) - vector.shift( - vector.center_func() - vector.get_start() - ) - return vector - - def get_circles(self, vectors): - return VGroup(*[ - self.get_circle( - vector, - color=color - ) - for vector, color in zip( - vectors, - self.get_color_iterator() - ) - ]) - - def get_circle(self, vector, color=BLUE): - circle = Circle(color=color, **self.circle_config) - circle.center_func = vector.get_start - circle.radius_func = vector.get_length - circle.add_updater(self.update_circle) - return circle - - def update_circle(self, circle): - circle.set_width(2 * circle.radius_func()) - circle.move_to(circle.center_func()) - return circle - - def get_vector_sum_path(self, vectors, color=YELLOW): - coefs = [v.coefficient for v in vectors] - freqs = [v.freq for v in vectors] - center = vectors[0].get_start() - - 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, vectors, stroke_width=2, **kwargs): - path = self.get_vector_sum_path(vectors, **kwargs) - broken_path = CurvesAsSubmobjects(path) - broken_path.curr_time = 0 - - def update_path(path, dt): - # alpha = path.curr_time * self.get_slow_factor() - alpha = self.get_vector_time() - 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 = stroke_width * (1 - (b % 1)) - sp.set_stroke(width=width) - path.curr_time += dt - return path - - broken_path.set_color(YELLOW) - broken_path.add_updater(update_path) - return broken_path - - def get_y_component_wave(self, - vectors, - left_x=1, - color=PINK, - n_copies=2, - right_shift_rate=5): - path = self.get_vector_sum_path(vectors) - 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.get_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.get_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, vectors, wave): - return DashedLine( - vectors[-1].get_end(), - wave[0].get_end(), - stroke_width=1, - dash_length=DEFAULT_DASH_LENGTH * 0.5, - ) - - # Computing Fourier series - # i.e. where all the math happens - 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_vectors": 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_vectors + 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_vectors": 8, - } - - -class FourierSeriesIntroBackground12(FourierSeriesIntroBackground4): - CONFIG = { - "n_vectors": 12, - } - - -class FourierSeriesIntroBackground20(FourierSeriesIntroBackground4): - CONFIG = { - "n_vectors": 20, - } - - -class FourierOfPiSymbol(FourierCirclesScene): - CONFIG = { - "n_vectors": 51, - "center_point": ORIGIN, - "slow_factor": 0.1, - "n_cycles": 1, - "tex": "\\pi", - "start_drawn": False, - "max_circle_stroke_width": 1, - } - - def construct(self): - self.add_vectors_circles_path() - for n in range(self.n_cycles): - self.run_one_cycle() - - def add_vectors_circles_path(self): - path = self.get_path() - coefs = self.get_coefficients_of_path(path) - vectors = self.get_rotating_vectors(coefficients=coefs) - circles = self.get_circles(vectors) - self.set_decreasing_stroke_widths(circles) - # approx_path = self.get_vector_sum_path(circles) - drawn_path = self.get_drawn_path(vectors) - if self.start_drawn: - self.vector_clock.increment_value(1) - - self.add(path) - self.add(vectors) - self.add(circles) - self.add(drawn_path) - - self.vectors = vectors - self.circles = circles - self.path = path - self.drawn_path = drawn_path - - def run_one_cycle(self): - time = 1 / self.slow_factor - self.wait(time) - - def set_decreasing_stroke_widths(self, circles): - mcsw = self.max_circle_stroke_width - for k, circle in zip(it.count(1), circles): - circle.set_stroke(width=max( - # mcsw / np.sqrt(k), - mcsw / k, - mcsw, - )) - return circles - - 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 FourierOfName(FourierOfPiSymbol): - CONFIG = { - "n_vectors": 100, - "name_color": WHITE, - "name_text": "Abc", - "time_per_symbol": 5, - "slow_factor": 1 / 5, - } - - def construct(self): - name = TextMobject(self.name_text) - max_width = FRAME_WIDTH - 2 - max_height = FRAME_HEIGHT - 2 - name.set_width(max_width) - if name.get_height() > max_height: - name.set_height(max_height) - - circles = VGroup(VectorizedPoint()) - for path in name.family_members_with_points(): - for subpath in path.get_subpaths(): - sp_mob = VMobject() - sp_mob.set_points(subpath) - coefs = self.get_coefficients_of_path(sp_mob) - new_circles = self.get_circles( - coefficients=coefs - ) - self.set_decreasing_stroke_widths(new_circles) - drawn_path = self.get_drawn_path(new_circles) - drawn_path.clear_updaters() - drawn_path.set_stroke(self.name_color, 3) - - new_circles.suspend_updating() - self.play(ReplacementTransform(circles, new_circles)) - new_circles.resume_updating() - circles = new_circles - self.play( - ShowCreation(drawn_path), - rate_func=linear, - run_time=self.time_per_symbol - ) - circles.suspend_updating() - self.play(FadeOut(circles)) - self.wait(3) - - -class FourierOfPiSymbol5(FourierOfPiSymbol): - CONFIG = { - "n_vectors": 5, - "run_time": 10, - } - - -class FourierOfTrebleClef(FourierOfPiSymbol): - CONFIG = { - "n_vectors": 101, - "run_time": 10, - "start_drawn": True, - "file_name": "TrebleClef", - "height": 7.5, - } - - def get_shape(self): - shape = SVGMobject(self.file_name) - return shape - - def get_path(self): - shape = self.get_shape() - path = shape.family_members_with_points()[0] - path.set_height(self.height) - path.set_fill(opacity=0) - path.set_stroke(WHITE, 0) - return path - - -class FourierOfIP(FourierOfTrebleClef): - CONFIG = { - "file_name": "IP_logo2", - "height": 6, - "n_vectors": 100, - } - - # def construct(self): - # path = self.get_path() - # self.add(path) - - def get_shape(self): - shape = SVGMobject(self.file_name) - return shape - - def get_path(self): - shape = self.get_shape() - path = shape.family_members_with_points()[0] - path.add_line_to(path.get_start()) - # path.make_smooth() - - path.set_height(self.height) - path.set_fill(opacity=0) - path.set_stroke(WHITE, 0) - return path - - -class FourierOfEighthNote(FourierOfTrebleClef): - CONFIG = { - "file_name": "EighthNote" - } - - -class FourierOfN(FourierOfTrebleClef): - CONFIG = { - "height": 6, - "n_vectors": 1000, - } - - def get_shape(self): - return TexMobject("N") - - -class FourierNailAndGear(FourierOfTrebleClef): - CONFIG = { - "height": 6, - "n_vectors": 200, - "run_time": 100, - "slow_factor": 0.01, - "parametric_function_step_size": 0.0001, - "arrow_config": { - "tip_length": 0.1, - "stroke_width": 2, - } - } - - def get_shape(self): - shape = SVGMobject("Nail_And_Gear")[1] - return shape - - -class FourierBatman(FourierOfTrebleClef): - CONFIG = { - "height": 4, - "n_vectors": 100, - "run_time": 10, - "arrow_config": { - "tip_length": 0.1, - "stroke_width": 2, - } - } - - def get_shape(self): - shape = SVGMobject("BatmanLogo")[1] - return shape - - -class FourierHeart(FourierOfTrebleClef): - CONFIG = { - "height": 4, - "n_vectors": 100, - "run_time": 10, - "arrow_config": { - "tip_length": 0.1, - "stroke_width": 2, - } - } - - def get_shape(self): - shape = SuitSymbol("hearts") - return shape - - def get_drawn_path(self, *args, **kwargs): - kwargs["stroke_width"] = 5 - path = super().get_drawn_path(*args, **kwargs) - path.set_color(PINK) - return path - - -class FourierNDQ(FourierOfTrebleClef): - CONFIG = { - "height": 4, - "n_vectors": 1000, - "run_time": 10, - "arrow_config": { - "tip_length": 0.1, - "stroke_width": 2, - } - } - - def get_shape(self): - path = VMobject() - shape = TexMobject("NDQ") - for sp in shape.family_members_with_points(): - path.append_points(sp.points) - return path - - -class FourierGoogleG(FourierOfTrebleClef): - CONFIG = { - "n_vectors": 10, - "height": 5, - "g_colors": [ - "#4285F4", - "#DB4437", - "#F4B400", - "#0F9D58", - ] - } - - def get_shape(self): - g = SVGMobject("google_logo")[5] - g.center() - self.add(g) - return g - - def get_drawn_path(self, *args, **kwargs): - kwargs["stroke_width"] = 7 - path = super().get_drawn_path(*args, **kwargs) - - blue, red, yellow, green = self.g_colors - - path[:250].set_color(blue) - path[250:333].set_color(green) - path[333:370].set_color(yellow) - path[370:755].set_color(red) - path[755:780].set_color(yellow) - path[780:860].set_color(green) - path[860:].set_color(blue) - - return path - - -class ExplainCircleAnimations(FourierCirclesScene): - CONFIG = { - "n_vectors": 100, - "center_point": 2 * DOWN, - "n_top_circles": 9, - "path_height": 3, - } - - def construct(self): - self.add_path() - self.add_circles() - self.wait(8) - self.organize_circles_in_a_row() - self.show_frequencies() - self.show_examples_for_frequencies() - self.show_as_vectors() - self.show_vector_sum() - 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) - - def organize_circles_in_a_row(self): - circles = self.circles - top_circles = circles[:self.n_top_circles].copy() - - 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) - - self.freq_numbers = freq_numbers - self.freq_word = freq_word - - 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) - self.play(FadeOut(self.freq_word)) - - def show_as_vectors(self): - top_circles = self.top_circles - top_vectors = self.get_rotating_vectors(top_circles) - top_vectors.set_color(WHITE) - - original_circles = top_circles.copy() - self.play( - FadeIn(top_vectors), - top_circles.set_opacity, 0, - ) - self.wait(3) - self.play( - top_circles.match_style, original_circles - ) - self.remove(top_vectors) - - self.top_vectors = top_vectors - - def show_vector_sum(self): - trackers = self.center_trackers.copy() - trackers.sort( - submob_func=lambda t: abs(t.circle.freq - 0.1) - ) - plane = self.plane = NumberPlane( - x_min=-3, - x_max=3, - y_min=-2, - y_max=2, - axis_config={ - "stroke_color": LIGHT_GREY, - } - ) - plane.set_stroke(width=1) - plane.fade(0.5) - plane.move_to(self.center_point) - - self.play( - FadeOut(self.drawn_path), - FadeOut(self.circles), - self.slow_factor_tracker.set_value, 0.05, - ) - self.add(plane, self.path) - self.play(FadeIn(plane)) - - new_circles = VGroup() - last_tracker = None - for tracker in trackers: - if last_tracker: - tracker.new_location_func = last_tracker.circle.get_start - else: - tracker.new_location_func = lambda: self.center_point - - original_circle = tracker.circle - tracker.circle = original_circle.copy() - tracker.circle.center_func = tracker.get_location - new_circles.add(tracker.circle) - - self.add(tracker, tracker.circle) - start_point = tracker.get_location() - self.play( - UpdateFromAlphaFunc( - tracker, lambda t, a: t.move_to( - interpolate( - start_point, - tracker.new_location_func(), - a, - ) - ), - run_time=2 - ) - ) - tracker.add_updater(lambda t: t.move_to( - t.new_location_func() - )) - self.wait(2) - last_tracker = tracker - - self.wait(3) - - self.clear() - self.slow_factor_tracker.set_value(0.1) - self.add( - self.top_circles, - self.freq_numbers, - self.path, - ) - self.add_circles() - for tc in self.top_circles: - for c in self.circles: - if c.freq == tc.freq: - tc.rotate( - angle_of_vector(c.get_start() - c.get_center()) - - angle_of_vector(tc.get_start() - tc.get_center()) - ) - self.wait(10) - - def tweak_starting_vectors(self): - top_circles = self.top_circles - circles = self.circles - path = self.path - drawn_path = self.drawn_path - - new_path = self.get_new_path() - new_coefs = self.get_coefficients_of_path(new_path) - new_circles = self.get_circles(coefficients=new_coefs) - - new_top_circles = VGroup() - new_top_vectors = VGroup() - for top_circle in top_circles: - for circle in new_circles: - if circle.freq == top_circle.freq: - new_top_circle = circle.copy() - new_top_circle.center_func = top_circle.get_center - new_top_vector = self.get_rotating_vector( - new_top_circle - ) - new_top_circles.add(new_top_circle) - new_top_vectors.add(new_top_vector) - - self.play( - self.slow_factor_tracker.set_value, 0, - FadeOut(drawn_path) - ) - self.wait() - self.play( - ReplacementTransform(top_circles, new_top_circles), - ReplacementTransform(circles, new_circles), - FadeOut(path), - run_time=3, - ) - new_drawn_path = self.get_drawn_path( - new_circles, stroke_width=4, - ) - self.add(new_drawn_path) - self.slow_factor_tracker.set_value(0.1) - self.wait(20) - - # - def configure_path(self, path): - path.set_stroke(WHITE, 1) - path.set_fill(BLACK, opacity=1) - path.set_height(self.path_height) - path.move_to(self.center_point) - return path - - def get_path(self): - tex = TexMobject("f") - path = tex.family_members_with_points()[0] - self.configure_path(path) - return path - # return Square().set_height(3) - - def get_new_path(self): - shape = SVGMobject("TrebleClef") - path = shape.family_members_with_points()[0] - self.configure_path(path) - path.scale(1.5, about_edge=DOWN) - return path diff --git a/active_projects/ode/part2/heat_equation.py b/active_projects/ode/part2/heat_equation.py deleted file mode 100644 index f50c1f41..00000000 --- a/active_projects/ode/part2/heat_equation.py +++ /dev/null @@ -1,2969 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part2.shared_constructs import * - - -class TwoDBodyWithManyTemperatures(ThreeDScene): - CONFIG = { - "cells_per_side": 20, - "body_height": 6, - } - - def construct(self): - self.introduce_body() - self.show_temperature_at_all_points() - - def introduce_body(self): - height = self.body_height - buff = 0.025 - rows = VGroup(*[ - VGroup(*[ - Dot( - # stroke_width=0.5, - stroke_width=0, - fill_opacity=1, - ) - for x in range(self.cells_per_side) - ]).arrange(RIGHT, buff=buff) - for y in range(self.cells_per_side) - ]).arrange(DOWN, buff=buff) - for row in rows[1::2]: - row.submobjects.reverse() - - body = self.body = VGroup(*it.chain(*rows)) - body.set_height(height) - body.center() - body.to_edge(LEFT) - - axes = self.axes = Axes( - x_min=-5, x_max=5, - y_min=-5, y_max=5, - ) - axes.match_height(body) - axes.move_to(body) - - for cell in body: - self.color_cell(cell) - # body.set_stroke(WHITE, 0.5) # Do this? - - plate = Square( - stroke_width=0, - fill_color=DARK_GREY, - sheen_direction=UL, - sheen_factor=1, - fill_opacity=1, - ) - plate.replace(body) - - plate_words = TextMobject("Piece of \\\\ metal") - plate_words.scale(2) - plate_words.set_stroke(BLACK, 2, background=True) - plate_words.set_color(BLACK) - plate_words.move_to(plate) - - self.play( - DrawBorderThenFill(plate), - Write( - plate_words, - run_time=2, - rate_func=squish_rate_func(smooth, 0.5, 1) - ) - ) - self.wait() - - self.remove(plate_words) - - def show_temperature_at_all_points(self): - body = self.body - start_corner = body[0].get_center() - - dot = Dot(radius=0.01, color=WHITE) - dot.move_to(start_corner) - - get_point = dot.get_center - - lhs = TexMobject("T = ") - lhs.next_to(body, RIGHT, LARGE_BUFF) - - decimal = DecimalNumber( - num_decimal_places=1, - unit="^\\circ" - ) - decimal.next_to(lhs, RIGHT, MED_SMALL_BUFF, DOWN) - decimal.add_updater( - lambda d: d.set_value( - 40 + 50 * self.point_to_temp(get_point()) - ) - ) - - arrow = Arrow(color=YELLOW) - arrow.set_stroke(BLACK, 8, background=True) - arrow.tip.set_stroke(BLACK, 2, background=True) - # arrow.add_to_back(arrow.copy().set_stroke(BLACK, 5)) - arrow.add_updater(lambda a: a.put_start_and_end_on( - lhs.get_left() + MED_SMALL_BUFF * LEFT, - get_point(), - )) - - dot.add_updater(lambda p: p.move_to( - body[-1] if (1 < len(body)) else start_corner - )) - self.add(body, dot, lhs, decimal, arrow) - self.play( - ShowIncreasingSubsets( - body, - run_time=10, - rate_func=linear, - ) - ) - self.wait() - self.remove(dot) - self.play( - FadeOut(arrow), - FadeOut(lhs), - FadeOut(decimal), - ) - - # - def point_to_temp(self, point, time=0): - x, y = self.axes.point_to_coords(point) - return two_d_temp_func( - 0.3 * x, 0.3 * y, t=time - ) - - def color_cell(self, cell, vect=RIGHT): - p0 = cell.get_corner(-vect) - p1 = cell.get_corner(vect) - colors = [] - for point in p0, p1: - temp = self.point_to_temp(point) - color = temperature_to_color(temp) - colors.append(color) - cell.set_color(color=colors) - cell.set_sheen_direction(vect) - return cell - - -class TwoDBodyWithManyTemperaturesGraph(ExternallyAnimatedScene): - pass - - -class TwoDBodyWithManyTemperaturesContour(ExternallyAnimatedScene): - pass - - -class BringTwoRodsTogether(Scene): - CONFIG = { - "step_size": 0.05, - "axes_config": { - "x_min": -1, - "x_max": 11, - "y_min": -10, - "y_max": 100, - "y_axis_config": { - "unit_size": 0.06, - "tick_frequency": 10, - }, - }, - "graph_x_min": 0, - "graph_x_max": 10, - "wait_time": 30, - "default_n_rod_pieces": 20, - "alpha": 1.0, - } - - def construct(self): - self.setup_axes() - self.setup_graph() - self.setup_clock() - - self.show_rods() - self.show_equilibration() - - def setup_axes(self): - axes = Axes(**self.axes_config) - axes.center().to_edge(UP) - - y_label = axes.get_y_axis_label("\\text{Temperature}") - y_label.to_edge(UP) - axes.y_axis.label = y_label - axes.y_axis.add(y_label) - axes.y_axis.add_numbers( - *range(20, 100, 20) - ) - - self.axes = axes - self.y_label = y_label - - def setup_graph(self): - graph = self.axes.get_graph( - self.initial_function, - x_min=self.graph_x_min, - x_max=self.graph_x_max, - step_size=self.step_size, - discontinuities=[5], - ) - graph.color_using_background_image("VerticalTempGradient") - - self.graph = graph - - def setup_clock(self): - clock = Clock() - clock.set_height(1) - clock.to_corner(UR) - clock.shift(MED_LARGE_BUFF * LEFT) - - time_lhs = TextMobject("Time: ") - time_label = DecimalNumber( - 0, num_decimal_places=2, - ) - time_rhs = TextMobject("s") - time_group = VGroup( - time_lhs, - time_label, - # time_rhs - ) - time_group.arrange(RIGHT, aligned_edge=DOWN) - time_rhs.shift(SMALL_BUFF * LEFT) - time_group.next_to(clock, DOWN) - - self.time_group = time_group - self.time_label = time_label - self.clock = clock - - def show_rods(self): - rod1, rod2 = rods = VGroup( - self.get_rod(0, 5), - self.get_rod(5, 10), - ) - rod1.set_color(rod1[0].get_color()) - rod2.set_color(rod2[-1].get_color()) - - rods.save_state() - rods.space_out_submobjects(1.5) - rods.center() - - labels = VGroup( - TexMobject("90^\\circ"), - TexMobject("10^\\circ"), - ) - for rod, label in zip(rods, labels): - label.next_to(rod, DOWN) - rod.label = label - - self.play( - FadeInFrom(rod1, UP), - Write(rod1.label), - ) - self.play( - FadeInFrom(rod2, DOWN), - Write(rod2.label) - ) - self.wait() - - self.rods = rods - self.rod_labels = labels - - def show_equilibration(self): - rods = self.rods - axes = self.axes - graph = self.graph - labels = self.rod_labels - self.play( - Write(axes), - rods.restore, - rods.space_out_submobjects, 1.1, - FadeIn(self.time_group), - FadeIn(self.clock), - *[ - MaintainPositionRelativeTo( - rod.label, rod - ) - for rod in rods - ], - ) - - br1 = Rectangle(height=0.2, width=1) - br1.set_stroke(width=0) - br1.set_fill(BLACK, opacity=1) - br2 = br1.copy() - br1.add_updater(lambda b: b.move_to(axes.c2p(0, 90))) - br1.add_updater( - lambda b: b.align_to(rods[0].get_right(), LEFT) - ) - br2.add_updater(lambda b: b.move_to(axes.c2p(0, 10))) - br2.add_updater( - lambda b: b.align_to(rods[1].get_left(), RIGHT) - ) - - self.add(graph, br1, br2) - self.play( - ShowCreation(graph), - labels[0].align_to, axes.c2p(0, 87), UP, - labels[1].align_to, axes.c2p(0, 13), DOWN, - ) - self.play() - self.play( - rods.restore, - rate_func=rush_into, - ) - self.remove(br1, br2) - - graph.add_updater(self.update_graph) - self.time_label.add_updater( - lambda d, dt: d.increment_value(dt) - ) - rods.add_updater(self.update_rods) - - self.play( - ClockPassesTime( - self.clock, - run_time=self.wait_time, - hours_passed=self.wait_time, - ), - FadeOut(labels) - ) - - # - def initial_function(self, x): - if x <= 5: - return 90 - else: - return 10 - - def update_graph(self, graph, dt, alpha=None, n_mini_steps=500): - if alpha is None: - alpha = self.alpha - points = np.append( - graph.get_start_anchors(), - [graph.get_last_point()], - axis=0, - ) - for k in range(n_mini_steps): - y_change = np.zeros(points.shape[0]) - dx = points[1][0] - points[0][0] - for i in range(len(points)): - p = points[i] - lp = points[max(i - 1, 0)] - rp = points[min(i + 1, len(points) - 1)] - d2y = (rp[1] - 2 * p[1] + lp[1]) - - if (0 < i < len(points) - 1): - second_deriv = d2y / (dx**2) - else: - second_deriv = 2 * d2y / (dx**2) - # second_deriv = 0 - - y_change[i] = alpha * second_deriv * dt / n_mini_steps - - # y_change[0] = y_change[1] - # y_change[-1] = y_change[-2] - # y_change[0] = 0 - # y_change[-1] = 0 - # y_change -= np.mean(y_change) - points[:, 1] += y_change - graph.set_points_smoothly(points) - return graph - - def get_second_derivative(self, x, dx=0.001): - graph = self.graph - x_min = self.graph_x_min - x_max = self.graph_x_max - - ly, y, ry = [ - graph.point_from_proportion( - inverse_interpolate(x_min, x_max, alt_x) - )[1] - for alt_x in (x - dx, x, x + dx) - ] - - # At the boundary, don't return the second deriv, - # but instead something matching the Neumann - # boundary condition. - if x == x_max: - return (ly - y) / dx - elif x == x_min: - return (ry - y) / dx - else: - d2y = ry - 2 * y + ly - return d2y / (dx**2) - - def get_rod(self, x_min, x_max, n_pieces=None): - if n_pieces is None: - n_pieces = self.default_n_rod_pieces - axes = self.axes - line = Line(axes.c2p(x_min, 0), axes.c2p(x_max, 0)) - rod = VGroup(*[ - Square() - for n in range(n_pieces) - ]) - rod.arrange(RIGHT, buff=0) - rod.match_width(line) - rod.set_height(0.2, stretch=True) - rod.move_to(axes.c2p(x_min, 0), LEFT) - rod.set_fill(opacity=1) - rod.set_stroke(width=1) - rod.set_sheen_direction(RIGHT) - self.color_rod_by_graph(rod) - return rod - - def update_rods(self, rods): - for rod in rods: - self.color_rod_by_graph(rod) - - def color_rod_by_graph(self, rod): - for piece in rod: - piece.set_color(color=[ - self.rod_point_to_color(piece.get_left()), - self.rod_point_to_color(piece.get_right()), - ]) - - def rod_point_to_graph_y(self, point): - axes = self.axes - x = axes.x_axis.p2n(point) - - graph = self.graph - alpha = inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - x, - ) - return axes.y_axis.p2n( - graph.point_from_proportion(alpha) - ) - - def y_to_color(self, y): - return temperature_to_color((y - 45) / 45) - - def rod_point_to_color(self, point): - return self.y_to_color( - self.rod_point_to_graph_y(point) - ) - - -class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether): - CONFIG = { - "alpha": 0.1, - "arrow_xs": np.linspace(0, 10, 22)[1:-1], - "arrow_scale_factor": 0.5, - "max_magnitude": 1.5, - "wait_time": 30, - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - (5, 0.3), - (7, 0.2), - (21, 0.1), - (41, 0.05), - ], - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_clock() - self.add_rod() - self.add_arrows() - self.initialize_updaters() - self.let_play() - - def add_axes(self): - self.setup_axes() - self.add(self.axes) - - def add_graph(self): - self.setup_graph() - self.add(self.graph) - - def add_clock(self): - self.setup_clock() - self.add(self.clock) - self.add(self.time_label) - self.time_label.next_to(self.clock, DOWN) - - def add_rod(self): - rod = self.rod = self.get_rod( - self.graph_x_min, - self.graph_x_max, - ) - self.add(rod) - - def add_arrows(self): - graph = self.graph - x_min = self.graph_x_min - x_max = self.graph_x_max - - xs = self.arrow_xs - arrows = VGroup(*[Vector(DOWN) for x in xs]) - asf = self.arrow_scale_factor - - def update_arrows(arrows): - for x, arrow in zip(xs, arrows): - d2y_dx2 = self.get_second_derivative(x) - mag = asf * np.sign(d2y_dx2) * abs(d2y_dx2) - mag = np.clip( - mag, - -self.max_magnitude, - self.max_magnitude, - ) - arrow.put_start_and_end_on( - ORIGIN, mag * UP - ) - point = graph.point_from_proportion( - inverse_interpolate(x_min, x_max, x) - ) - arrow.shift(point - arrow.get_start()) - arrow.set_color( - self.rod_point_to_color(point) - ) - - arrows.add_updater(update_arrows) - - self.add(arrows) - self.arrows = arrows - - def initialize_updaters(self): - if hasattr(self, "graph"): - self.graph.add_updater(self.update_graph) - if hasattr(self, "rod"): - self.rod.add_updater(self.color_rod_by_graph) - if hasattr(self, "time_label"): - self.time_label.add_updater( - lambda d, dt: d.increment_value(dt) - ) - - def let_play(self): - self.run_clock(self.wait_time) - - def run_clock(self, time): - self.play( - ClockPassesTime( - self.clock, - run_time=time, - hours_passed=time, - ), - ) - - # - def temp_func(self, x, t): - new_x = TAU * x / 10 - return 50 + 20 * np.sum([ - amp * np.sin(freq * new_x) * - np.exp(-(self.alpha * freq**2) * t) - for freq, amp in self.freq_amplitude_pairs - ]) - - def initial_function(self, x, time=0): - return self.temp_func(x, 0) - - -class TalkThrough1DHeatGraph(ShowEvolvingTempGraphWithArrows, SpecialThreeDScene): - CONFIG = { - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - (5, 0.3), - (7, 0.2), - ], - "surface_resolution": 20, - "graph_slice_step": 10 / 20, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_rod() - - self.emphasize_graph() - self.emphasize_rod() - self.show_x_axis() - self.show_changes_over_time() - self.show_surface() - - def add_graph(self): - self.graph = self.get_graph() - self.add(self.graph) - - def emphasize_graph(self): - graph = self.graph - q_marks = VGroup(*[ - TexMobject("?").move_to( - graph.point_from_proportion(a), - UP, - ).set_stroke(BLACK, 3, background=True) - for a in np.linspace(0, 1, 20) - ]) - - self.play(LaggedStart(*[ - Succession( - FadeInFromLarge(q_mark), - FadeOutAndShift(q_mark, DOWN), - ) - for q_mark in q_marks - ])) - self.wait() - - def emphasize_rod(self): - alt_rod = self.get_rod(0, 10, 50) - word = TextMobject("Rod") - word.scale(2) - word.next_to(alt_rod, UP, MED_SMALL_BUFF) - - self.play( - LaggedStart( - *[ - Rotating(piece, rate_func=smooth) - for piece in alt_rod - ], - run_time=2, - lag_ratio=0.01, - ), - Write(word) - ) - self.remove(*alt_rod) - self.wait() - - self.rod_word = word - - def show_x_axis(self): - rod = self.rod - axes = self.axes - graph = self.graph - x_axis = axes.x_axis - x_numbers = x_axis.get_number_mobjects(*range(1, 11)) - x_axis_label = TexMobject("x") - x_axis_label.next_to(x_axis.get_right(), UP) - - self.play( - rod.set_opacity, 0.5, - FadeInFrom(x_axis_label, UL), - LaggedStartMap( - FadeInFrom, x_numbers, - lambda m: (m, UP), - ) - ) - self.wait() - - # Show x-values - triangle = ArrowTip( - start_angle=-90 * DEGREES, - color=LIGHT_GREY, - ) - x_tracker = ValueTracker(PI) - get_x = x_tracker.get_value - - def get_x_point(): - return x_axis.n2p(get_x()) - - def get_graph_point(): - return graph.point_from_proportion( - inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - get_x(), - ) - ) - - triangle.add_updater( - lambda t: t.next_to(get_x_point(), UP) - ) - x_label = VGroup( - TexMobject("x"), - TexMobject("="), - DecimalNumber( - 0, - num_decimal_places=3, - include_background_rectangle=True, - ).scale(0.9) - ) - x_label.set_stroke(BLACK, 5, background=True) - x_label.add_updater(lambda m: m[-1].set_value(get_x())) - x_label.add_updater(lambda m: m.arrange(RIGHT, buff=SMALL_BUFF)) - x_label.add_updater(lambda m: m[-1].align_to(m[0], DOWN)) - x_label.add_updater(lambda m: m.next_to(triangle, UP, SMALL_BUFF)) - x_label.add_updater(lambda m: m.shift(SMALL_BUFF * RIGHT)) - rod_piece = always_redraw( - lambda: self.get_rod( - get_x() - 0.05, get_x() + 0.05, - n_pieces=1, - ) - ) - - self.play( - FadeInFrom(triangle, UP), - FadeIn(x_label), - FadeIn(rod_piece), - FadeOut(self.rod_word), - ) - for value in [np.exp(2), (np.sqrt(5) + 1) / 2]: - self.play(x_tracker.set_value, value, run_time=2) - self.wait() - - # Show graph - v_line = always_redraw( - lambda: DashedLine( - get_x_point(), - get_graph_point(), - color=self.rod_point_to_color(get_x_point()), - ) - ) - graph_dot = Dot() - graph_dot.add_updater( - lambda m: m.set_color( - self.rod_point_to_color(m.get_center()) - ) - ) - graph_dot.add_updater( - lambda m: m.move_to(get_graph_point()) - ) - t_label = TexMobject("T(", "x", ")") - t_label.set_stroke(BLACK, 3, background=True) - t_label.add_updater( - lambda m: m.next_to(graph_dot, UR, buff=0) - ) - - self.add(v_line, rod_piece, x_label, triangle) - self.play( - TransformFromCopy(x_label[0], t_label[1]), - FadeIn(t_label[0::2]), - ShowCreation(v_line), - GrowFromPoint(graph_dot, get_x_point()), - ) - self.add(t_label) - self.wait() - self.play( - x_tracker.set_value, TAU, - run_time=5, - ) - - self.x_tracker = x_tracker - self.graph_label_group = VGroup( - v_line, rod_piece, triangle, x_label, - graph_dot, t_label, - ) - self.set_variables_as_attrs(*self.graph_label_group) - self.set_variables_as_attrs(x_numbers, x_axis_label) - - def show_changes_over_time(self): - graph = self.graph - t_label = self.t_label - new_t_label = TexMobject("T(", "x", ",", "t", ")") - new_t_label.set_color_by_tex("t", YELLOW) - new_t_label.match_updaters(t_label) - - self.setup_clock() - clock = self.clock - time_label = self.time_label - time_group = self.time_group - - time = 5 - self.play( - FadeIn(clock), - FadeIn(time_group), - ) - self.play( - self.get_graph_time_change_animation( - graph, time - ), - ClockPassesTime(clock), - ChangeDecimalToValue( - time_label, time, - rate_func=linear, - ), - ReplacementTransform( - t_label, - new_t_label, - rate_func=squish_rate_func(smooth, 0.5, 0.7), - ), - run_time=time - ) - self.play( - ShowCreationThenFadeAround( - new_t_label.get_part_by_tex("t") - ), - ) - self.wait() - self.play( - FadeOut(clock), - ChangeDecimalToValue(time_label, 0), - VFadeOut(time_group), - self.get_graph_time_change_animation( - graph, - new_time=0, - ), - run_time=1, - rate_func=smooth, - ) - - self.graph_label_group.remove(t_label) - self.graph_label_group.add(new_t_label) - - def show_surface(self): - axes = self.axes - graph = self.graph - t_min = 0 - t_max = 10 - - axes_copy = axes.deepcopy() - self.original_axes = self.axes - - # Set rod final state - final_graph = self.get_graph(t_max) - curr_graph = self.graph - self.graph = final_graph - final_rod = self.rod.copy() - final_rod.set_opacity(1) - self.color_rod_by_graph(final_rod) - self.graph = curr_graph - - # Time axis - t_axis = NumberLine( - x_min=t_min, - x_max=t_max, - ) - origin = axes.c2p(0, 0) - t_axis.shift(origin - t_axis.n2p(0)) - t_axis.add_numbers( - *range(1, t_max + 1), - direction=UP, - ) - time_label = TextMobject("Time") - time_label.scale(1.5) - time_label.next_to(t_axis, UP) - t_axis.time_label = time_label - t_axis.add(time_label) - # t_axis.rotate(90 * DEGREES, LEFT, about_point=origin) - t_axis.rotate(90 * DEGREES, UP, about_point=origin) - - # New parts of graph - step = self.graph_slice_step - graph_slices = VGroup(*[ - self.get_graph(time=t).shift( - t * IN - ) - for t in np.arange(0, t_max + step, step) - ]) - graph_slices.set_stroke(width=1) - graph_slices.set_shade_in_3d(True) - - # Input plane - x_axis = self.axes.x_axis - y = axes.c2p(0, 0)[1] - surface_config = { - "u_min": self.graph_x_min, - "u_max": self.graph_x_max, - "v_min": t_min, - "v_max": t_max, - "resolution": self.surface_resolution, - } - input_plane = ParametricSurface( - lambda x, t: np.array([ - x_axis.n2p(x)[0], - y, - t_axis.n2p(t)[2], - ]), - **surface_config, - ) - input_plane.set_style( - fill_opacity=0.5, - fill_color=BLUE_B, - stroke_width=0.5, - stroke_color=WHITE, - ) - - # Surface - y_axis = axes.y_axis - surface = ParametricSurface( - lambda x, t: np.array([ - x_axis.n2p(x)[0], - y_axis.n2p(self.temp_func(x, t))[1], - t_axis.n2p(t)[2], - ]), - **surface_config, - ) - surface.set_style( - fill_opacity=0.1, - fill_color=LIGHT_GREY, - stroke_width=0.5, - stroke_color=WHITE, - stroke_opacity=0.5, - ) - - # Rotate everything on screen and move camera - # in such a way that it looks the same - curr_group = Group(*self.get_mobjects()) - curr_group.clear_updaters() - self.set_camera_orientation( - phi=90 * DEGREES, - ) - mobs = [ - curr_group, - graph_slices, - t_axis, - input_plane, - surface, - ] - for mob in mobs: - self.orient_mobject_for_3d(mob) - - # Clean current mobjects - self.x_label.set_stroke(BLACK, 2, background=True) - self.x_label[-1][0].fade(1) - - self.move_camera( - phi=80 * DEGREES, - theta=-85 * DEGREES, - added_anims=[ - Write(input_plane), - Write(t_axis), - FadeOut(self.graph_label_group), - self.rod.set_opacity, 1, - ] - ) - self.begin_ambient_camera_rotation() - self.add(*graph_slices, *self.get_mobjects()) - self.play( - FadeIn(surface), - LaggedStart(*[ - TransformFromCopy(graph, graph_slice) - for graph_slice in graph_slices - ], lag_ratio=0.02) - ) - self.wait(4) - - # Show slices - self.axes = axes_copy # So get_graph works... - slicing_plane = Rectangle( - stroke_width=0, - fill_color=WHITE, - fill_opacity=0.2, - ) - slicing_plane.set_shade_in_3d(True) - slicing_plane.replace( - Line(axes_copy.c2p(0, 0), axes_copy.c2p(10, 100)), - stretch=True - ) - self.orient_mobject_for_3d(slicing_plane) - - def get_time_slice(t): - new_slice = self.get_graph(t) - new_slice.set_shade_in_3d(True) - self.orient_mobject_for_3d(new_slice) - new_slice.shift(t * UP) - return new_slice - - graph.set_shade_in_3d(True) - t_tracker = ValueTracker(0) - graph.add_updater(lambda g: g.become( - get_time_slice(t_tracker.get_value()) - )) - - self.orient_mobject_for_3d(final_rod) - final_rod.shift(10 * UP) - kw = {"run_time": 10, "rate_func": linear} - self.rod.save_state() - self.play( - ApplyMethod(t_tracker.set_value, 10, **kw), - Transform(self.rod, final_rod, **kw), - ApplyMethod(slicing_plane.shift, 10 * UP, **kw), - ) - self.wait() - - self.set_variables_as_attrs( - t_axis, - input_plane, - surface, - graph_slices, - slicing_plane, - t_tracker, - ) - - # - def get_graph(self, time=0): - graph = self.axes.get_graph( - lambda x: self.temp_func(x, time), - x_min=self.graph_x_min, - x_max=self.graph_x_max, - step_size=self.step_size, - ) - graph.time = time - graph.color_using_background_image("VerticalTempGradient") - return graph - - def get_graph_time_change_animation(self, graph, new_time, **kwargs): - old_time = graph.time - graph.time = new_time - config = { - "run_time": abs(new_time - old_time), - "rate_func": linear, - } - config.update(kwargs) - - return UpdateFromAlphaFunc( - graph, - lambda g, a: g.become( - self.get_graph(interpolate( - old_time, new_time, a - )) - ), - **config - ) - - def orient_mobject_for_3d(self, mob): - mob.rotate( - 90 * DEGREES, - axis=RIGHT, - about_point=ORIGIN - ) - return mob - - -class ContrastXChangesToTChanges(TalkThrough1DHeatGraph): - CONFIG = { - # "surface_resolution": 5, - # "graph_slice_step": 1, - } - - def construct(self): - self.catchup_with_last_scene() - self.emphasize_dimensions_of_input_space() - self.reset_time_to_zero() - - self.show_changes_with_x() - self.show_changes_with_t() - - def catchup_with_last_scene(self): - self.force_skipping() - - self.add_axes() - self.add_graph() - self.add_rod() - - self.rod_word = Point() - self.show_x_axis() - self.show_surface() - - self.revert_to_original_skipping_status() - - def emphasize_dimensions_of_input_space(self): - plane = self.input_plane - plane_copy = plane.copy() - plane_copy.set_color(BLUE_E) - plane_copy.shift(SMALL_BUFF * 0.5 * OUT) - - plane_copy1 = plane_copy.copy() - plane_copy1.stretch(0.01, 1, about_edge=DOWN) - plane_copy0 = plane_copy1.copy() - plane_copy0.stretch(0, 0, about_edge=LEFT) - - words = TextMobject("2d input\\\\space") - words.scale(2) - words.move_to(plane.get_center() + SMALL_BUFF * OUT) - - self.play( - Write(words), - self.camera.phi_tracker.set_value, 60 * DEGREES, - self.camera.theta_tracker.set_value, -90 * DEGREES, - run_time=1 - ) - self.play( - ReplacementTransform(plane_copy0, plane_copy1) - ) - self.play( - ReplacementTransform(plane_copy1, plane_copy) - ) - self.wait(2) - self.play(FadeOut(plane_copy)) - - self.input_plane_words = words - - def reset_time_to_zero(self): - self.play( - self.t_tracker.set_value, 0, - VFadeOut(self.slicing_plane), - Restore(self.rod), - ) - - def show_changes_with_x(self): - alpha_tracker = ValueTracker(0) - line = always_redraw( - lambda: self.get_tangent_line( - self.graph, alpha_tracker.get_value(), - ) - ) - - self.stop_ambient_camera_rotation() - self.play( - ShowCreation(line), - FadeOut(self.input_plane_words), - self.camera.phi_tracker.set_value, 80 * DEGREES, - self.camera.theta_tracker.set_value, -90 * DEGREES, - ) - self.play( - alpha_tracker.set_value, 0.425, - run_time=5, - rate_func=bezier([0, 0, 1, 1]), - ) - - # Show dx and dT - p0 = line.point_from_proportion(0.3) - p2 = line.point_from_proportion(0.7) - p1 = np.array([p2[0], *p0[1:]]) - dx_line = DashedLine(p0, p1) - dT_line = DashedLine(p1, p2) - dx = TexMobject("dx") - dT = TexMobject("dT") - VGroup(dx, dT).scale(0.7) - VGroup(dx, dT).rotate(90 * DEGREES, RIGHT) - dx.next_to(dx_line, IN, SMALL_BUFF) - dT.next_to(dT_line, RIGHT, SMALL_BUFF) - - self.play( - ShowCreation(dx_line), - FadeInFrom(dx, LEFT) - ) - self.wait() - self.play( - ShowCreation(dT_line), - FadeInFrom(dT, IN) - ) - self.wait() - self.play(*map(FadeOut, [ - line, dx_line, dT_line, dx, dT, - ])) - - def show_changes_with_t(self): - slices = self.graph_slices - slice_alpha = 0.075 - graph = VMobject() - graph.set_points_smoothly([ - gs.point_from_proportion(slice_alpha) - for gs in slices - ]) - graph.color_using_background_image("VerticalTempGradient") - graph.set_shade_in_3d(True) - - alpha_tracker = ValueTracker(0) - line = always_redraw( - lambda: self.get_tangent_line( - graph, alpha_tracker.get_value(), - ) - ) - - plane = Square() - plane.set_stroke(width=0) - plane.set_fill(WHITE, 0.1) - plane.set_shade_in_3d(True) - plane.rotate(90 * DEGREES, RIGHT) - plane.rotate(90 * DEGREES, OUT) - plane.set_height(10) - plane.set_depth(8, stretch=True) - plane.move_to(self.t_axis.n2p(0), IN + DOWN) - plane.shift(RIGHT) - - self.play( - self.camera.theta_tracker.set_value, -20 * DEGREES, - self.camera.frame_center.shift, 4 * LEFT, - ) - - self.play( - ShowCreation( - graph.copy(), - remover=True - ), - FadeInFrom(plane, 6 * DOWN, run_time=2), - VFadeIn(line), - ApplyMethod( - alpha_tracker.set_value, 1, - run_time=8, - ), - ) - self.add(graph) - - self.begin_ambient_camera_rotation(-0.02) - self.camera.frame_center.add_updater( - lambda m, dt: m.shift(0.05 * dt * RIGHT) - ) - - self.play( - FadeOut(line), - FadeOut(plane), - ) - self.wait(30) # Let rotate - - self.t_graph = graph - - # - def get_tangent_line(self, graph, alpha, d_alpha=0.001, length=2): - if alpha < 1 - d_alpha: - a1 = alpha - a2 = alpha + d_alpha - else: - a1 = alpha - d_alpha - a2 = alpha - - p1 = graph.point_from_proportion(a1) - p2 = graph.point_from_proportion(a2) - line = Line(p1, p2, color=WHITE) - line.scale( - length / line.get_length() - ) - line.move_to(p1) - return line - - -class TransitionToTempVsTime(ContrastXChangesToTChanges): - CONFIG = { - # "surface_resolution": 5, - # "graph_slice_step": 1, - } - - def construct(self): - self.catchup_with_last_scene() - - axes = self.original_axes - t_axis = self.t_axis - y_axis = axes.y_axis - x_axis = axes.x_axis - - for mob in self.get_mobjects(): - mob.clear_updaters() - self.stop_ambient_camera_rotation() - self.move_camera( - phi=90 * DEGREES, - theta=0 * DEGREES, - added_anims=[ - Rotate( - y_axis, 90 * DEGREES, - axis=OUT, - about_point=y_axis.n2p(0), - ), - FadeOut(VGroup( - self.graph_slices, - self.surface, - self.slicing_plane, - self.rod, - self.graph, - self.x_numbers, - self.x_axis_label, - self.t_graph, - )), - self.camera.frame_center.move_to, 5 * LEFT, - ] - ) - self.play( - VGroup(x_axis, self.input_plane).stretch, - 0, 0, {"about_point": y_axis.n2p(0)}, - ) - self.play( - t_axis.time_label.scale, 1 / 1.5, - t_axis.time_label.next_to, t_axis, IN, MED_LARGE_BUFF, - t_axis.numbers.shift, 0.7 * IN, - ) - self.wait() - - def catchup_with_last_scene(self): - self.force_skipping() - - self.add_axes() - self.add_graph() - self.add_rod() - - self.rod_word = Point() - self.show_x_axis() - self.show_surface() - - self.emphasize_dimensions_of_input_space() - self.reset_time_to_zero() - - self.show_changes_with_x() - self.show_changes_with_t() - - self.revert_to_original_skipping_status() - - -class ShowDelTermsAsTinyNudges(TransitionToTempVsTime): - CONFIG = { - # "surface_resolution": 5, - # "graph_slice_step": 1, - "tangent_line_length": 4, - } - - def construct(self): - self.catchup_with_last_scene() - self.stop_camera() - self.show_del_t() - self.show_del_x() - - def stop_camera(self): - self.stop_ambient_camera_rotation() - for mob in self.get_mobjects(): - mob.clear_updaters() - - def show_del_x(self): - x_tracker = ValueTracker(3) - dx_tracker = ValueTracker(0.5) - - line_group = self.get_line_group( - self.graph, - x_tracker, - dx_tracker, - corner_index=0, - ) - dx_line, dT_line, tan_line = line_group - - del_x = TexMobject("\\partial x") - del_x.set_color(GREEN) - del_x.line = dx_line - del_x.direction = OUT - del_T = TexMobject("\\partial T") - del_T.line = dT_line - del_T.direction = RIGHT - syms = VGroup(del_T, del_x) - for sym in syms: - sym.add_updater(lambda m: m.set_width( - dx_line.get_length() - )) - sym.rect = SurroundingRectangle(sym) - group = VGroup(sym, sym.rect) - group.rotate(90 * DEGREES, RIGHT) - - for sym in syms: - sym.add_updater(lambda m: m.next_to( - m.line, m.direction, SMALL_BUFF, - )) - sym.rect.move_to(sym) - - self.move_camera( - phi=80 * DEGREES, - theta=-90 * DEGREES, - added_anims=[ - self.camera.frame_center.move_to, ORIGIN, - ], - ) - for sym in reversed(syms): - self.play( - FadeInFrom(sym, -sym.direction), - ShowCreation( - sym.line.copy(), - remover=True - ), - ) - self.add(sym.line) - self.play(ShowCreation(tan_line)) - for sym in syms: - self.play( - ShowCreationThenDestruction(sym.rect) - ) - self.wait() - self.wait() - self.add(line_group) - self.play( - dx_tracker.set_value, 0.01, - run_time=5, - ) - self.play( - FadeOut(syms), - FadeOut(line_group), - ) - - def show_del_t(self): - # Largely copy pasted from above. - # Reconsolidate if any of this will actually - # be used later. - t_tracker = ValueTracker(1) - dt_tracker = ValueTracker(1) - - line_group = self.get_line_group( - self.t_graph, t_tracker, dt_tracker, - corner_index=1, - ) - dt_line, dT_line, tan_line = line_group - - del_t = TexMobject("\\partial t") - del_t.set_color(YELLOW) - del_t.line = dt_line - del_t.direction = OUT - del_T = TexMobject("\\partial T") - del_T.line = dT_line - del_T.direction = UP - syms = VGroup(del_T, del_t) - for sym in syms: - sym.rect = SurroundingRectangle(sym) - group = VGroup(sym, sym.rect) - group.rotate(90 * DEGREES, RIGHT) - group.rotate(90 * DEGREES, OUT) - sym.add_updater(lambda m: m.set_height( - 0.8 * dT_line.get_length() - )) - - del_t.add_updater(lambda m: m.set_height( - min(0.5, m.line.get_length()) - )) - del_T.add_updater(lambda m: m.set_depth( - min(0.5, m.line.get_length()) - )) - for sym in syms: - sym.add_updater(lambda m: m.next_to( - m.line, m.direction, SMALL_BUFF, - )) - sym.rect.move_to(sym) - - self.move_camera( - phi=80 * DEGREES, - theta=-10 * DEGREES, - added_anims=[ - self.camera.frame_center.move_to, 5 * LEFT, - ], - ) - for sym in reversed(syms): - self.play( - FadeInFrom(sym, -sym.direction), - ShowCreation( - sym.line.copy(), - remover=True - ), - ) - self.add(sym.line) - self.play(ShowCreation(tan_line)) - for sym in syms: - self.play( - ShowCreationThenDestruction(sym.rect) - ) - self.wait() - self.wait() - self.add(line_group) - self.play( - dt_tracker.set_value, 0.01, - run_time=5, - ) - self.play( - FadeOut(syms), - FadeOut(line_group), - ) - - # - def get_line_group(self, graph, input_tracker, nudge_tracker, corner_index): - get_x = input_tracker.get_value - get_dx = nudge_tracker.get_value - - def get_graph_point(x): - return graph.point_from_proportion( - inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - x, - ) - ) - - def get_corner(p1, p2): - result = np.array(p1) - result[corner_index] = p2[corner_index] - return result - - line_group = VGroup( - Line(color=WHITE), - Line(color=RED), - Line(color=WHITE, stroke_width=2), - ) - - def update_line_group(group): - dxl, dTl, tl = group - p0 = get_graph_point(get_x()) - p2 = get_graph_point(get_x() + get_dx()) - p1 = get_corner(p0, p2) - - dxl.set_points_as_corners([p0, p1]) - dTl.set_points_as_corners([p1, p2]) - tl.set_points_as_corners([p0, p2]) - tl.scale( - self.tangent_line_length / tl.get_length() - ) - line_group.add_updater(update_line_group) - return line_group - - -class ShowCurvatureToRateOfChangeIntuition(ShowEvolvingTempGraphWithArrows): - CONFIG = { - "freq_amplitude_pairs": [ - (1, 0.7), - (2, 1), - (3, 0.5), - (4, 0.3), - (5, 0.3), - (7, 0.2), - ], - "arrow_xs": [0.7, 3.8, 4.6, 5.4, 6.2, 9.3], - "arrow_scale_factor": 0.2, - "max_magnitude": 1.0, - "wait_time": 20, - } - - def let_play(self): - arrows = self.arrows - curves = VGroup(*[ - self.get_mini_curve( - inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - x, - ) - ) - for x in self.arrow_xs - ]) - curves.set_stroke(WHITE, 5) - - curve_words = VGroup() - for curve, arrow in zip(curves, arrows): - word = TextMobject("curve") - word.scale(0.7) - word.next_to(curve, arrow.get_vector()[1] * DOWN, SMALL_BUFF) - curve_words.add(word) - - self.remove(arrows) - - self.play( - ShowCreation(curves), - LaggedStartMap(FadeIn, curve_words), - self.y_label.set_fill, {"opacity": 0}, - ) - self.wait() - self.add(*arrows, curves) - self.play(LaggedStartMap(GrowArrow, arrows)) - self.wait() - - self.play(FadeOut(VGroup(curves, curve_words))) - self.add(arrows) - super().let_play() - - def get_mini_curve(self, alpha, d_alpha=0.02): - result = VMobject() - result.pointwise_become_partial( - self.graph, - alpha - d_alpha, - alpha + d_alpha, - ) - return result - - -class DiscreteSetup(ShowEvolvingTempGraphWithArrows): - CONFIG = { - "step_size": 1, - "rod_piece_size_ratio": 1 / 3, - "dashed_line_stroke_opacity": 1.0, - "dot_radius": DEFAULT_DOT_RADIUS, - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - (5, 0.3), - (7, 0.2), - (21, 0.1), - # (41, 0.05), - ], - } - - def construct(self): - self.add_axes() - self.add_graph() - self.discretize() - self.let_time_pass() - self.show_nieghbor_rule() - self.focus_on_three_points() - self.show_difference_formula() - self.gut_check_new_interpretation() - self.write_second_difference() - self.emphasize_final_expression() - - def add_axes(self): - super().add_axes() - self.axes.shift(MED_SMALL_BUFF * LEFT) - - def add_graph(self): - points = self.get_points(time=0) - graph = VMobject() - graph.set_points_smoothly(points) - graph.color_using_background_image("VerticalTempGradient") - - self.add(graph) - - self.graph = graph - self.points = points - - def discretize(self): - axes = self.axes - x_axis = axes.x_axis - graph = self.graph - - piecewise_graph = CurvesAsSubmobjects(graph) - dots = self.get_dots() - v_lines = VGroup(*map(self.get_v_line, dots)) - - rod_pieces = VGroup() - for x in self.get_sample_inputs(): - piece = Line(LEFT, RIGHT) - piece.set_width( - self.step_size * self.rod_piece_size_ratio - ) - piece.move_to(axes.c2p(x, 0)) - piece.set_color( - self.rod_point_to_color(piece.get_center()) - ) - rod_pieces.add(piece) - - word = TextMobject("Discrete version") - word.scale(1.5) - word.next_to(x_axis, UP) - word.set_stroke(BLACK, 3, background=True) - - self.remove(graph) - self.play( - ReplacementTransform( - piecewise_graph, dots, - ), - Write(word, run_time=1) - ) - self.add(v_lines, word) - self.play( - x_axis.fade, 0.8, - TransformFromCopy( - x_axis.tick_marks[1:], - rod_pieces, - ), - LaggedStartMap(ShowCreation, v_lines) - ) - self.play(FadeOut(word)) - self.wait() - - self.rod_pieces = rod_pieces - self.dots = dots - self.v_lines = v_lines - - def let_time_pass(self): - dots = self.dots - - t_tracker = ValueTracker(0) - t_tracker.add_updater(lambda m, dt: m.increment_value(dt)) - self.add(t_tracker) - - self.add_clock() - self.time_label.next_to(self.clock, DOWN) - self.time_label.add_updater( - lambda m: m.set_value(t_tracker.get_value()) - ) - dots.add_updater(lambda d: d.become( - self.get_dots(t_tracker.get_value()) - )) - run_time = 5 - self.play( - ClockPassesTime( - self.clock, - run_time=run_time, - hours_passed=run_time, - ), - ) - t_tracker.clear_updaters() - t_tracker.set_value(run_time) - self.wait() - self.play( - t_tracker.set_value, 0, - FadeOut(self.clock), - FadeOut(self.time_label), - ) - self.remove(t_tracker) - dots.clear_updaters() - - def show_nieghbor_rule(self): - dots = self.dots - rod_pieces = self.rod_pieces - index = self.index = 2 - - p1, p2, p3 = rod_pieces[index:index + 3] - d1, d2, d3 = dots[index:index + 3] - point_label = TextMobject("Point") - neighbors_label = TextMobject("Neighbors") - words = VGroup(point_label, neighbors_label) - for word in words: - word.scale(0.7) - word.add_background_rectangle() - - point_label.next_to(p2, DOWN) - neighbors_label.next_to(p2, UP, buff=1) - bottom = neighbors_label.get_bottom() - kw = { - "buff": 0.1, - "stroke_width": 2, - "tip_length": 0.15 - } - arrows = VGroup( - Arrow(bottom, p1.get_center(), **kw), - Arrow(bottom, p3.get_center(), **kw), - ) - arrows.set_color(WHITE) - - dot = Dot() - dot.set_fill(GREY, opacity=0.2) - dot.replace(p2) - dot.scale(3) - - self.play( - dot.scale, 0, - dot.set_opacity, 0, - FadeInFrom(point_label, DOWN) - ) - self.play( - FadeInFrom(neighbors_label, DOWN), - *map(GrowArrow, arrows) - ) - self.wait() - - # Let d2 change - self.play( - d1.set_y, 3, - d3.set_y, 3, - ) - - def get_v(): - return 0.25 * np.sum([ - d1.get_y(), - -2 * d2.get_y(), - + d3.get_y(), - ]) - v_vect_fader = ValueTracker(0) - v_vect = always_redraw( - lambda: Vector( - get_v() * UP, - color=temperature_to_color( - get_v(), -2, 2, - ), - ).shift(d2.get_center()).set_opacity( - v_vect_fader.get_value(), - ) - ) - d2.add_updater( - lambda d, dt: d.shift( - get_v() * dt * UP, - ) - ) - - self.add(v_vect) - self.play(v_vect_fader.set_value, 1) - self.wait(3) - self.play( - d1.set_y, 0, - d3.set_y, 0, - ) - self.wait(4) - self.play(FadeOut(VGroup( - point_label, - neighbors_label, - arrows - ))) - - self.v_vect = v_vect - self.example_pieces = VGroup(p1, p2, p3) - self.example_dots = VGroup(d1, d2, d3) - - def focus_on_three_points(self): - dots = self.example_dots - d1, d2, d3 = dots - pieces = self.example_pieces - y_axis = self.axes.y_axis - - x_labels, T_labels = [ - VGroup(*[ - TexMobject("{}_{}".format(s, i)) - for i in [1, 2, 3] - ]).scale(0.8) - for s in ("x", "T") - ] - for xl, piece in zip(x_labels, pieces): - xl.next_to(piece, DOWN) - xl.add_background_rectangle() - for Tl, dot in zip(T_labels, dots): - Tl.dot = dot - Tl.add_updater(lambda m: m.next_to( - m.dot, RIGHT, SMALL_BUFF - )) - Tl.add_background_rectangle() - T1, T2, T3 = T_labels - - d2.movement_updater = d2.get_updaters()[0] - dots.clear_updaters() - self.remove(self.v_vect) - - self.play( - ShowCreationThenFadeAround(pieces), - FadeOut(self.dots[:self.index]), - FadeOut(self.v_lines[:self.index]), - FadeOut(self.rod_pieces[:self.index]), - FadeOut(self.dots[self.index + 3:]), - FadeOut(self.v_lines[self.index + 3:]), - FadeOut(self.rod_pieces[self.index + 3:]), - ) - self.play(LaggedStartMap( - FadeInFrom, x_labels, - lambda m: (m, LEFT), - lag_ratio=0.3, - run_time=2, - )) - self.play( - d3.set_y, 1, - d2.set_y, 0.25, - d1.set_y, 0, - ) - self.wait() - self.play(LaggedStart(*[ - TransformFromCopy(xl, Tl) - for xl, Tl in zip(x_labels, T_labels) - ], lag_ratio=0.3, run_time=2)) - self.wait() - - # Show lines - h_lines = VGroup(*map(self.get_h_line, dots)) - hl1, hl2, hl3 = h_lines - - average_pointer = ArrowTip( - start_angle=0, - length=0.2, - ) - average_pointer.set_color(YELLOW) - average_pointer.stretch(0.25, 1) - average_pointer.add_updater( - lambda m: m.move_to( - 0.5 * (hl1.get_start() + hl3.get_start()), - RIGHT - ) - ) - average_arrows = always_redraw(lambda: VGroup(*[ - Arrow( - hl.get_start(), - average_pointer.get_right(), - color=WHITE, - buff=0.0, - ) - for hl in [hl1, hl3] - ])) - average_label = TexMobject( - "{T_1", "+", "T_3", "\\over", "2}" - ) - average_label.scale(0.5) - average_label.add_updater(lambda m: m.next_to( - average_pointer, LEFT, SMALL_BUFF - )) - - average_rect = SurroundingRectangle(average_label) - average_rect.add_updater( - lambda m: m.move_to(average_label) - ) - average_words = TextMobject("Neighbor\\\\average") - average_words.match_width(average_rect) - average_words.match_color(average_rect) - average_words.add_updater( - lambda m: m.next_to(average_rect, UP, SMALL_BUFF) - ) - - mini_T1 = average_label.get_part_by_tex("T_1") - mini_T3 = average_label.get_part_by_tex("T_3") - for mini, line in (mini_T1, hl1), (mini_T3, hl3): - mini.save_state() - mini.next_to(line, LEFT, SMALL_BUFF) - - self.add(hl1, hl3, T_labels) - y_axis.remove(y_axis.numbers) - self.play( - GrowFromPoint(hl1, hl1.get_end()), - GrowFromPoint(hl3, hl3.get_end()), - TransformFromCopy( - T1, mini_T1, - ), - TransformFromCopy( - T3, mini_T3, - ), - FadeOut(y_axis.numbers), - y_axis.set_stroke, {"width": 1}, - ) - self.play( - FadeIn(average_pointer), - Restore(mini_T1), - Restore(mini_T3), - FadeIn(average_label[1]), - FadeIn(average_label[3:]), - *map(GrowArrow, average_arrows) - ) - self.add(average_arrows, average_label) - self.play( - ShowCreation(average_rect), - FadeIn(average_words), - ) - self.play( - GrowFromPoint(hl2, hl2.get_end()) - ) - self.wait() - - # Show formula - formula = TexMobject( - "\\left(", - "{T_1", "+", "T_3", "\\over", "2}", - "-", "T_2", - "\\right)" - ) - formula.to_corner(UR, buff=MED_LARGE_BUFF) - formula.shift(1.7 * LEFT) - brace = Brace(formula, DOWN) - diff_value = DecimalNumber(include_sign=True) - diff_value.add_updater(lambda m: m.set_value( - y_axis.p2n(average_pointer.get_right()) - - y_axis.p2n(d2.get_center()) - )) - diff_value.next_to(brace, DOWN) - - self.play( - ReplacementTransform( - average_label.deepcopy(), - formula[1:1 + len(average_label)] - ), - TransformFromCopy(T2, formula[-2]), - FadeIn(formula[-3]), - FadeIn(formula[-1]), - FadeIn(formula[0]), - GrowFromCenter(brace), - FadeIn(diff_value) - ) - self.wait() - - # Changes - self.play(FadeIn(self.v_vect)) - d2.add_updater(d2.movement_updater) - self.wait(5) - - self.play( - d3.set_y, 3, - d1.set_y, 2.5, - d2.set_y, -2, - ) - self.wait(5) - self.play( - d3.set_y, 1, - d1.set_y, -1, - ) - self.wait(8) - - # Show derivative - lhs = TexMobject( - "{dT_2", "\\over", "dt}", "=", "\\alpha" - ) - dt = lhs.get_part_by_tex("dt") - alpha = lhs.get_part_by_tex("\\alpha") - lhs.next_to(formula, LEFT, SMALL_BUFF) - - self.play(Write(lhs)) - self.play(ShowCreationThenFadeAround(dt)) - self.wait() - self.play(ShowCreationThenFadeAround(alpha)) - self.wait() - self.play( - FadeOut(brace), - FadeOut(diff_value), - ) - - self.lhs = lhs - self.rhs = formula - - def show_difference_formula(self): - lhs = self.lhs - rhs = self.rhs - d1, d2, d3 = self.example_dots - - new_rhs = TexMobject( - "=", - "{\\alpha", "\\over", "2}", - "\\left(", - "(", "T_3", "-", "T_2", ")", - "-", - "(", "T_2", "-", "T_1", ")", - "\\right)" - ) - big_parens = VGroup( - new_rhs.get_part_by_tex("\\left("), - new_rhs.get_part_by_tex("\\right)"), - ) - for paren in big_parens: - paren.scale(2) - new_rhs.next_to(rhs, DOWN) - new_rhs.align_to(lhs.get_part_by_tex("="), LEFT) - - def p2p_anim(mob1, mob2, tex, index=0): - return TransformFromCopy( - mob1.get_parts_by_tex(tex)[index], - mob2.get_parts_by_tex(tex)[index], - ) - - self.play( - p2p_anim(lhs, new_rhs, "="), - p2p_anim(rhs, new_rhs, "\\left("), - p2p_anim(rhs, new_rhs, "\\right)"), - p2p_anim(lhs, new_rhs, "\\alpha"), - p2p_anim(rhs, new_rhs, "\\over"), - p2p_anim(rhs, new_rhs, "2"), - ) - self.play( - p2p_anim(rhs, new_rhs, "T_3"), - p2p_anim(rhs, new_rhs, "-"), - p2p_anim(rhs, new_rhs, "T_2"), - FadeIn(new_rhs.get_parts_by_tex("(")[1]), - FadeIn(new_rhs.get_parts_by_tex(")")[0]), - ) - self.play( - p2p_anim(rhs, new_rhs, "T_2", -1), - p2p_anim(rhs, new_rhs, "-", -1), - p2p_anim(rhs, new_rhs, "T_1"), - FadeIn(new_rhs.get_parts_by_tex("-")[1]), - FadeIn(new_rhs.get_parts_by_tex("(")[2]), - FadeIn(new_rhs.get_parts_by_tex(")")[1]), - ) - self.wait() - - self.rhs2 = new_rhs - - # Show deltas - T1_index = new_rhs.index_of_part_by_tex("T_1") - T3_index = new_rhs.index_of_part_by_tex("T_3") - diff1 = new_rhs[T1_index - 2:T1_index + 1] - diff2 = new_rhs[T3_index:T3_index + 3] - brace1 = Brace(diff1, DOWN, buff=SMALL_BUFF) - brace2 = Brace(diff2, DOWN, buff=SMALL_BUFF) - delta_T1 = TexMobject("\\Delta T_1") - delta_T1.next_to(brace1, DOWN, SMALL_BUFF) - delta_T2 = TexMobject("\\Delta T_2") - delta_T2.next_to(brace2, DOWN, SMALL_BUFF) - minus = TexMobject("-") - minus.move_to(Line( - delta_T1.get_right(), - delta_T2.get_left(), - )) - braces = VGroup(brace1, brace2) - deltas = VGroup(delta_T1, delta_T2) - - kw = { - "direction": LEFT, - "buff": SMALL_BUFF, - "min_num_quads": 2, - } - lil_brace1 = always_redraw(lambda: Brace( - Line(d1.get_left(), d2.get_left()), **kw - )) - lil_brace2 = always_redraw(lambda: Brace( - Line(d2.get_left(), d3.get_left()), **kw - )) - lil_braces = VGroup(lil_brace1, lil_brace2) - lil_delta_T1 = delta_T1.copy() - lil_delta_T2 = delta_T2.copy() - lil_deltas = VGroup(lil_delta_T1, lil_delta_T2) - for brace, delta in zip(lil_braces, lil_deltas): - delta.brace = brace - delta.add_updater(lambda d: d.next_to( - d.brace, LEFT, SMALL_BUFF, - )) - - delta_T1.set_color(BLUE) - lil_delta_T1.set_color(BLUE) - delta_T2.set_color(RED) - lil_delta_T2.set_color(RED) - - double_difference_brace = Brace(deltas, DOWN) - double_difference_words = TextMobject( - "Difference of differences" - ) - double_difference_words.next_to( - double_difference_brace, DOWN - ) - - self.play( - GrowFromCenter(brace1), - GrowFromCenter(lil_brace1), - FadeIn(delta_T1), - FadeIn(lil_delta_T1), - ) - self.wait() - self.play( - GrowFromCenter(brace2), - GrowFromCenter(lil_brace2), - FadeIn(delta_T2), - FadeIn(lil_delta_T2), - ) - self.wait() - self.play( - Write(minus), - GrowFromCenter(double_difference_brace), - Write(double_difference_words), - ) - self.wait() - - self.braces = braces - self.deltas = deltas - self.delta_minus = minus - self.lil_braces = lil_braces - self.lil_deltas = lil_deltas - self.double_difference_brace = double_difference_brace - self.double_difference_words = double_difference_words - - def gut_check_new_interpretation(self): - lil_deltas = self.lil_deltas - d1, d2, d3 = self.example_dots - - self.play(ShowCreationThenFadeAround(lil_deltas[0])) - self.play(ShowCreationThenFadeAround(lil_deltas[1])) - self.wait() - self.play( - d2.shift, MED_SMALL_BUFF * UP, - rate_func=there_and_back, - ) - self.wait() - self.play( - d3.set_y, 3, - d1.set_y, -0.5, - ) - self.wait(5) - self.play( - d3.set_y, 1.5, - d1.set_y, -2, - ) - self.wait(5) - - def write_second_difference(self): - dd_word = self.double_difference_words - - delta_delta = TexMobject("\\Delta \\Delta T_1") - delta_delta.set_color(MAROON_B) - - delta_delta.move_to(dd_word, UP) - - second_difference_word = TextMobject( - "``Second difference''" - ) - second_difference_word.next_to(delta_delta, DOWN) - - self.play( - FadeOutAndShift(dd_word, UP), - FadeInFrom(delta_delta, UP), - ) - self.wait() - self.play( - Write(second_difference_word), - ) - self.wait() - - # Random play - d1, d2, d3 = self.example_dots - self.play( - d3.set_y, 3, - d1.set_y, -0.5, - ) - self.wait(5) - self.play( - d3.set_y, 1.5, - d1.set_y, -2, - ) - self.wait(5) - - self.delta_delta = delta_delta - self.second_difference_word = second_difference_word - - def emphasize_final_expression(self): - lhs = self.lhs - rhs = self.rhs - rhs2 = self.rhs2 - old_dd = self.delta_delta - dd = old_dd.copy() - old_ao2 = rhs2[1:4] - ao2 = old_ao2.copy() - - new_lhs = lhs[:-1] - full_rhs = VGroup( - lhs[-1], - lhs[-2].copy(), - rhs, - rhs2, - self.braces, - self.deltas, - self.delta_minus, - self.double_difference_brace, - old_dd, - self.second_difference_word, - ) - new_rhs = VGroup(ao2, dd) - new_rhs.arrange(RIGHT, buff=SMALL_BUFF) - new_rhs.next_to(new_lhs, RIGHT) - - self.play( - full_rhs.to_edge, DOWN, {"buff": LARGE_BUFF}, - ) - self.play( - TransformFromCopy(old_ao2, ao2), - TransformFromCopy(old_dd, dd), - ) - self.play( - ShowCreationThenFadeAround( - VGroup(new_lhs, new_rhs) - ) - ) - self.wait() - - # - def get_sample_inputs(self): - return np.arange( - self.graph_x_min, - self.graph_x_max + self.step_size, - self.step_size, - ) - - def get_points(self, time=0): - return [ - self.axes.c2p(x, self.temp_func(x, t=time)) - for x in self.get_sample_inputs() - ] - - def get_dots(self, time=0): - points = self.get_points(time) - dots = VGroup(*[ - Dot( - point, - radius=self.dot_radius - ) - for point in points - ]) - dots.color_using_background_image("VerticalTempGradient") - return dots - - def get_dot_dashed_line(self, dot, index, color=False): - direction = np.zeros(3) - direction[index] = -1 - - def get_line(): - p1 = dot.get_edge_center(direction) - p0 = np.array(p1) - p0[index] = self.axes.c2p(0, 0)[index] - result = DashedLine( - p0, p1, - stroke_width=2, - color=WHITE, - stroke_opacity=self.dashed_line_stroke_opacity, - ) - if color: - result.color_using_background_image( - "VerticalTempGradient" - ) - return result - return always_redraw(get_line) - - def get_h_line(self, dot): - return self.get_dot_dashed_line(dot, 0, True) - - def get_v_line(self, dot): - return self.get_dot_dashed_line(dot, 1) - - -class ShowFinitelyManyX(DiscreteSetup): - def construct(self): - self.setup_axes() - axes = self.axes - axes.fade(1) - points = [ - axes.c2p(x, 0) - for x in self.get_sample_inputs()[1:] - ] - x_labels = VGroup(*[ - TexMobject("x_{}".format(i)).next_to( - p, DOWN - ) - for i, p in enumerate(points) - ]) - - self.play(LaggedStartMap( - FadeInFromLarge, x_labels - )) - self.play(LaggedStartMap(FadeOut, x_labels)) - self.wait() - - -class DiscreteGraphStillImage1(DiscreteSetup): - CONFIG = { - "step_size": 1, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.discretize() - - -class DiscreteGraphStillImageFourth(DiscreteGraphStillImage1): - CONFIG = { - "step_size": 0.25, - } - - -class DiscreteGraphStillImageTenth(DiscreteGraphStillImage1): - CONFIG = { - "step_size": 0.1, - "dashed_line_stroke_opacity": 0.25, - "dot_radius": 0.04, - } - - -class DiscreteGraphStillImageHundredth(DiscreteGraphStillImage1): - CONFIG = { - "step_size": 0.01, - "dashed_line_stroke_opacity": 0.1, - "dot_radius": 0.01, - } - - -class TransitionToContinuousCase(DiscreteSetup): - CONFIG = { - "step_size": 0.1, - "tangent_line_length": 3, - "wait_time": 30, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.show_temperature_difference() - self.show_second_derivative() - self.show_curvature_examples() - self.show_time_changes() - - def add_graph(self): - self.setup_graph() - self.play( - ShowCreation( - self.graph, - run_time=3, - ) - ) - self.wait() - - def show_temperature_difference(self): - x_tracker = ValueTracker(2) - dx_tracker = ValueTracker(1) - - line_group = self.get_line_group( - x_tracker, - dx_tracker, - ) - dx_line, dT_line, tan_line, dx_sym, dT_sym = line_group - tan_line.set_stroke(width=0) - - brace = Brace(dx_line, UP) - fixed_distance = TextMobject("Fixed\\\\distance") - fixed_distance.scale(0.7) - fixed_distance.next_to(brace, UP) - delta_T = TexMobject("\\Delta T") - delta_T.move_to(dT_sym, LEFT) - - self.play( - ShowCreation(VGroup(dx_line, dT_line)), - FadeInFrom(delta_T, LEFT) - ) - self.play( - GrowFromCenter(brace), - FadeInFromDown(fixed_distance), - ) - self.wait() - self.play( - FadeOut(delta_T, UP), - FadeIn(dT_sym, DOWN), - FadeOut(brace, UP), - FadeOut(fixed_distance, UP), - FadeIn(dx_sym, DOWN), - ) - self.add(line_group) - self.play( - dx_tracker.set_value, 0.01, - run_time=5 - ) - self.wait() - self.play( - dx_tracker.set_value, 0.3, - ) - - # Show rate of change - to_zero = TexMobject("\\rightarrow 0") - to_zero.match_height(dT_sym) - to_zero.next_to(dT_sym, buff=SMALL_BUFF) - - ratio = TexMobject( - "{\\partial T", "\\over", "\\partial x}" - ) - ratio[0].match_style(dT_sym) - ratio.to_edge(UP) - - self.play(ShowCreationThenFadeAround( - dT_sym, - surrounding_rectangle_config={ - "buff": 0.05, - "stroke_width": 1, - } - )) - self.play(GrowFromPoint(to_zero, dT_sym.get_right())) - self.wait() - self.play( - TransformFromCopy( - dT_sym, - ratio.get_part_by_tex("\\partial T") - ), - TransformFromCopy( - dx_sym, - ratio.get_part_by_tex("\\partial x") - ), - Write(ratio.get_part_by_tex("\\over")) - ) - self.play( - ShowCreation( - tan_line.copy().set_stroke(width=2), - remover=True - ), - FadeOut(to_zero), - ) - tan_line.set_stroke(width=2) - self.wait() - - # Look at neighbors - x0 = x_tracker.get_value() - dx = dx_tracker.get_value() - v_line, lv_line, rv_line = v_lines = VGroup(*[ - self.get_v_line(x) - for x in [x0, x0 - dx, x0 + dx] - ]) - v_lines[1:].set_color(BLUE) - - self.play(ShowCreation(v_line)) - self.play( - TransformFromCopy(v_line, lv_line), - TransformFromCopy(v_line, rv_line), - ) - self.wait() - self.play( - FadeOut(v_lines[1:]), - ApplyMethod( - dx_tracker.set_value, 0.01, - run_time=2 - ), - ) - - self.line_group = line_group - self.deriv = ratio - self.x_tracker = x_tracker - self.dx_tracker = dx_tracker - self.v_line = v_line - - def show_second_derivative(self): - x_tracker = self.x_tracker - deriv = self.deriv - v_line = self.v_line - - deriv_of_deriv = TexMobject( - "{\\partial", - "\\left(", - "{\\partial T", "\\over", "\\partial x}", - "\\right)", - "\\over", - "\\partial x}" - ) - deriv_of_deriv.set_color_by_tex("\\partial T", RED) - - deriv_of_deriv.to_edge(UP) - dT_index = deriv_of_deriv.index_of_part_by_tex("\\partial T") - inner_deriv = deriv_of_deriv[dT_index:dT_index + 3] - - self.play( - ReplacementTransform(deriv, inner_deriv), - Write(VGroup(*filter( - lambda m: m not in inner_deriv, - deriv_of_deriv, - ))) - ) - v_line.add_updater(lambda m: m.become( - self.get_v_line(x_tracker.get_value()) - )) - for change in [-0.1, 0.1]: - self.play( - x_tracker.increment_value, change, - run_time=3 - ) - - # Write second deriv - second_deriv = TexMobject( - "{\\partial^2 T", "\\over", "\\partial x^2}" - ) - second_deriv[0].set_color(RED) - eq = TexMobject("=") - eq.next_to(deriv_of_deriv, RIGHT) - second_deriv.next_to(eq, RIGHT) - second_deriv.align_to(deriv_of_deriv, DOWN) - eq.match_y(second_deriv.get_part_by_tex("\\over")) - - self.play(Write(eq)) - self.play( - TransformFromCopy( - deriv_of_deriv.get_parts_by_tex("\\partial")[:2], - second_deriv.get_parts_by_tex("\\partial^2 T"), - ), - ) - self.play( - Write(second_deriv.get_part_by_tex("\\over")), - TransformFromCopy( - deriv_of_deriv.get_parts_by_tex("\\partial x"), - second_deriv.get_parts_by_tex("\\partial x"), - ), - ) - self.wait() - - def show_curvature_examples(self): - x_tracker = self.x_tracker - v_line = self.v_line - line_group = self.line_group - - x_tracker.set_value(3.6) - self.wait() - self.play( - x_tracker.set_value, 3.8, - run_time=4, - ) - self.wait() - x_tracker.set_value(6.2) - self.wait() - self.play( - x_tracker.set_value, 6.4, - run_time=4, - ) - self.wait() - - # - dx = 0.2 - neighbor_lines = always_redraw(lambda: VGroup(*[ - self.get_v_line( - x_tracker.get_value() + u * dx, - line_class=Line, - ) - for u in [-1, 1] - ])) - neighbor_lines.set_color(BLUE) - - self.play(FadeOut(line_group)) - self.play(*[ - TransformFromCopy(v_line, nl) - for nl in neighbor_lines - ]) - self.add(neighbor_lines) - self.play( - x_tracker.set_value, 5, - run_time=5, - rate_func=lambda t: smooth(t, 3) - ) - v_line.clear_updaters() - self.play( - FadeOut(v_line), - FadeOut(neighbor_lines), - ) - self.wait() - - def show_time_changes(self): - self.setup_clock() - graph = self.graph - - time_label = self.time_label - clock = self.clock - time_label.next_to(clock, DOWN) - - graph.add_updater(self.update_graph) - time_label.add_updater( - lambda d, dt: d.increment_value(dt) - ) - - self.add(time_label) - self.add_arrows() - self.play( - ClockPassesTime( - clock, - run_time=self.wait_time, - hours_passed=self.wait_time, - ), - ) - - # - def get_v_line(self, x, line_class=DashedLine, stroke_width=2): - axes = self.axes - graph = self.graph - line = line_class( - axes.c2p(x, 0), - graph.point_from_proportion( - inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - x, - ) - ), - stroke_width=stroke_width, - ) - return line - - def get_line_group(self, - x_tracker, - dx_tracker, - dx_tex="\\partial x", - dT_tex="\\partial T", - max_sym_width=0.5, - ): - graph = self.graph - get_x = x_tracker.get_value - get_dx = dx_tracker.get_value - - dx_line = Line(color=WHITE) - dT_line = Line(color=RED) - tan_line = Line(color=WHITE) - lines = VGroup(dx_line, dT_line, tan_line) - lines.set_stroke(width=2) - dx_sym = TexMobject(dx_tex) - dT_sym = TexMobject(dT_tex) - dT_sym.match_color(dT_line) - syms = VGroup(dx_sym, dT_sym) - - group = VGroup(*lines, *syms) - - def update_group(group): - dxl, dTl, tanl, dxs, dTs = group - x = get_x() - dx = get_dx() - p0, p2 = [ - graph.point_from_proportion( - inverse_interpolate( - self.graph_x_min, - self.graph_x_max, - x - ) - ) - for x in [x, x + dx] - ] - p1 = np.array([p2[0], *p0[1:]]) - dxl.put_start_and_end_on(p0, p1) - dTl.put_start_and_end_on(p1, p2) - tanl.put_start_and_end_on(p0, p2) - tanl.scale( - self.tangent_line_length / - tanl.get_length() - ) - dxs.match_width(dxl) - dTs.set_height(0.7 * dTl.get_height()) - for sym in dxs, dTs: - if sym.get_width() > max_sym_width: - sym.set_width(max_sym_width) - dxs.next_to( - dxl, -dTl.get_vector(), SMALL_BUFF, - ) - dTs.next_to( - dTl, dxl.get_vector(), SMALL_BUFF, - ) - - group.add_updater(update_group) - return group - - -class ShowManyVLines(TransitionToContinuousCase): - CONFIG = { - "wait_time": 20, - "max_denom": 10, - "x_step": 0.025, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_v_lines() - self.show_time_changes() - - def add_arrows(self): - pass - - def add_v_lines(self): - axes = self.axes - - v_lines = always_redraw(lambda: VGroup(*[ - self.get_v_line( - x, - line_class=Line, - stroke_width=0.5, - ) - for x in np.arange(0, 10, self.x_step) - ])) - group = VGroup(*v_lines) - - x_pointer = ArrowTip(start_angle=PI / 2) - x_pointer.set_color(WHITE) - x_pointer.next_to(axes.c2p(0, 0), DOWN, buff=0) - x_eq = VGroup( - TexMobject("x="), - DecimalNumber(0) - ) - x_eq.add_updater( - lambda m: m.arrange(RIGHT, buff=SMALL_BUFF) - ) - x_eq.add_updater( - lambda m: m[1].set_value(axes.x_axis.p2n(x_pointer.get_top())) - ) - x_eq.add_updater(lambda m: m.next_to( - x_pointer, DOWN, SMALL_BUFF, - submobject_to_align=x_eq[0] - )) - - self.add(x_pointer, x_eq) - self.play( - Write( - group, - remover=True, - lag_ratio=self.x_step / 2, - run_time=6, - ), - ApplyMethod( - x_pointer.next_to, - axes.c2p(10, 0), - DOWN, {"buff": 0}, - rate_func=linear, - run_time=5, - ), - ) - self.add(v_lines) - x_eq.clear_updaters() - self.play( - FadeOut(x_eq), - FadeOut(x_pointer), - ) - - -class ShowNewtonsLawGraph(Scene): - CONFIG = { - "k": 0.2, - "initial_water_temp": 80, - "room_temp": 20, - "delta_T_color": YELLOW, - } - - def construct(self): - self.setup_axes() - self.show_temperatures() - self.show_graph() - self.show_equation() - self.talk_through_examples() - - def setup_axes(self): - axes = Axes( - x_min=0, - x_max=10, - y_min=0, - y_max=100, - y_axis_config={ - "unit_size": 0.06, - "tick_frequency": 10, - }, - center_point=5 * LEFT + 2.5 * DOWN - ) - x_axis = axes.x_axis - y_axis = axes.y_axis - y_axis.add_numbers(*range(20, 100, 20)) - x_axis.add_numbers(*range(1, 11)) - - x_axis.label = TextMobject("Time") - x_axis.label.next_to(x_axis, DOWN, MED_SMALL_BUFF) - - y_axis.label = TexMobject("\\text{Temperature}") - y_axis.label.next_to(y_axis, RIGHT, buff=SMALL_BUFF) - y_axis.label.align_to(axes, UP) - for axis in [x_axis, y_axis]: - axis.add(axis.label) - - self.add(axes) - self.axes = axes - - def show_temperatures(self): - axes = self.axes - - water_dot = Dot() - water_dot.color_using_background_image("VerticalTempGradient") - water_dot.move_to(axes.c2p(0, self.initial_water_temp)) - room_line = DashedLine( - axes.c2p(0, self.room_temp), - axes.c2p(10, self.room_temp), - ) - room_line.set_color(BLUE) - room_line.color_using_background_image("VerticalTempGradient") - - water_arrow = Vector(LEFT, color=WHITE) - water_arrow.next_to(water_dot, RIGHT, SMALL_BUFF) - water_words = TextMobject( - "Initial water\\\\temperature" - ) - water_words.scale(0.7) - water_words.next_to(water_arrow, RIGHT) - - room_words = TextMobject("Room temperature") - room_words.scale(0.7) - room_words.next_to(room_line, DOWN, SMALL_BUFF) - - self.play( - FadeInFrom(water_dot, RIGHT), - GrowArrow(water_arrow), - Write(water_words), - run_time=1, - ) - self.play(ShowCreation(room_line)) - self.play(FadeInFromDown(room_words)) - self.wait() - - self.set_variables_as_attrs( - water_dot, - water_arrow, - water_words, - room_line, - room_words, - ) - - def show_graph(self): - axes = self.axes - water_dot = self.water_dot - - k = self.k - rt = self.room_temp - t0 = self.initial_water_temp - graph = axes.get_graph( - lambda t: rt + (t0 - rt) * np.exp(-k * t) - ) - graph.color_using_background_image("VerticalTempGradient") - - def get_x(): - return axes.x_axis.p2n(water_dot.get_center()) - - brace_line = always_redraw(lambda: Line( - axes.c2p(get_x(), rt), - water_dot.get_center(), - stroke_width=0, - )) - brace = always_redraw( - lambda: Brace( - brace_line, RIGHT, buff=SMALL_BUFF - ) - ) - - delta_T = TexMobject("\\Delta T") - delta_T.set_color(self.delta_T_color) - delta_T.add_updater(lambda m: m.next_to( - brace, RIGHT, SMALL_BUFF - )) - - self.add(brace_line) - self.play( - GrowFromCenter(brace), - Write(delta_T), - ) - self.play( - ShowCreation(graph), - UpdateFromFunc( - water_dot, - lambda m: m.move_to(graph.get_end()) - ), - run_time=10, - rate_func=linear, - ) - self.wait() - - self.graph = graph - self.brace = brace - self.delta_T = delta_T - - def show_equation(self): - delta_T = self.delta_T - - equation = TexMobject( - "{d ({\\Delta T}) \\over dt} = -k \\cdot {\\Delta T}", - tex_to_color_map={ - "{\\Delta T}": self.delta_T_color, - "-k": WHITE, - "=": WHITE, - } - ) - equation.to_corner(UR) - equation.shift(LEFT) - - delta_T_parts = equation.get_parts_by_tex("\\Delta T") - eq_i = equation.index_of_part_by_tex("=") - deriv = equation[:eq_i] - prop_to = equation.get_part_by_tex("-k") - parts = VGroup(deriv, prop_to, delta_T_parts[1]) - - words = TextMobject( - "Rate of change", - "is proportional to", - "itself", - ) - words.scale(0.7) - words.next_to(equation, DOWN) - colors = [BLUE, WHITE, YELLOW] - for part, word, color in zip(parts, words, colors): - part.word = word - word.set_color(color) - word.save_state() - words[0].next_to(parts[0], DOWN) - - self.play( - TransformFromCopy( - VGroup(delta_T), - delta_T_parts, - ), - Write(VGroup(*filter( - lambda p: p not in delta_T_parts, - equation - ))) - ) - - rects = VGroup() - for part in parts: - rect = SurroundingRectangle( - part, - color=part.word.get_color(), - buff=SMALL_BUFF, - stroke_width=2, - ) - anims = [ - ShowCreation(rect), - FadeIn(part.word), - ] - if part is parts[1]: - anims.append(Restore(words[0])) - self.play(*anims) - rects.add(rect) - - self.play(FadeOut(rects, lag_ratio=0.2)) - - self.equation = equation - self.equation_words = words - - def talk_through_examples(self): - dot = self.water_dot - graph = self.graph - - self.play( - MoveAlongPath( - dot, graph, - rate_func=lambda t: smooth(1 - t), - run_time=2, - ) - ) - - # - def get_slope_line(self, graph, x): - pass diff --git a/active_projects/ode/part2/pi_scenes.py b/active_projects/ode/part2/pi_scenes.py deleted file mode 100644 index c044996b..00000000 --- a/active_projects/ode/part2/pi_scenes.py +++ /dev/null @@ -1,142 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part2.wordy_scenes import WriteHeatEquationTemplate - - -class ReactionsToInitialHeatEquation(PiCreatureScene): - def construct(self): - randy = self.pi_creature - randy.set_color(BLUE_C) - randy.center() - - point = VectorizedPoint().next_to(randy, UL, LARGE_BUFF) - randy.add_updater(lambda r: r.look_at(point)) - - self.play(randy.change, "horrified") - self.wait() - self.play(randy.change, "pondering") - self.wait() - self.play( - randy.change, "confused", - point.next_to, randy, UR, LARGE_BUFF, - ) - self.wait(2) - self.play( - point.shift, 2 * DOWN, - randy.change, "horrified" - ) - self.wait(4) - - -class ContrastPDEToODE(TeacherStudentsScene): - CONFIG = { - "random_seed": 2, - } - - def construct(self): - student = self.students[2] - pde, ode = words = VGroup(*[ - TextMobject( - text + "\\\\", - "Differential\\\\", - "Equation" - ) - for text in ("Partial", "Ordinary") - ]) - pde[0].set_color(YELLOW) - ode[0].set_color(BLUE) - for word in words: - word.arrange(DOWN, aligned_edge=LEFT) - - words.arrange(RIGHT, buff=LARGE_BUFF) - words.next_to(student.get_corner(UR), UP, MED_LARGE_BUFF) - words.shift(UR) - lt = TexMobject("<") - lt.scale(1.5) - lt.move_to(Line(pde.get_right(), ode.get_left())) - - for pi in self.pi_creatures: - pi.add_updater(lambda p: p.look_at(pde)) - - self.play( - FadeInFromDown(VGroup(words, lt)), - student.change, "raise_right_hand", - ) - self.play( - self.get_student_changes("pondering", "pondering", "hooray"), - self.teacher.change, "happy" - ) - self.wait(3) - self.play( - Swap(ode, pde), - self.teacher.change, "raise_right_hand", - self.get_student_changes( - "erm", "sassy", "confused" - ) - ) - self.look_at(words) - self.change_student_modes( - "thinking", "thinking", "tease", - ) - self.wait(3) - - -class AskAboutWhereEquationComesFrom(TeacherStudentsScene, WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d1_equation() - equation.move_to(self.hold_up_spot, DOWN) - - self.play( - FadeInFromDown(equation), - self.teacher.change, "raise_right_hand" - ) - self.student_says( - "Um...why?", - target_mode="sassy", - student_index=2, - bubble_kwargs={"direction": RIGHT}, - ) - self.change_student_modes( - "confused", "confused", "sassy", - ) - self.wait() - self.play( - self.teacher.change, "pondering", - ) - self.wait(2) - - -class AskWhyRewriteIt(TeacherStudentsScene): - def construct(self): - self.student_says( - "Why?", student_index=1, - bubble_kwargs={"height": 2, "width": 2}, - ) - self.students[1].bubble = None - self.teacher_says( - "One step closer\\\\to derivatives" - ) - self.change_student_modes( - "thinking", "thinking", "thinking", - look_at_arg=4 * LEFT + 2 * UP - ) - self.wait(2) - - -class ReferenceKhanVideo(TeacherStudentsScene): - def construct(self): - khan_logo = ImageMobject("KhanLogo") - khan_logo.set_height(1) - khan_logo.next_to(self.teacher, UP, buff=2) - khan_logo.shift(2 * LEFT) - - self.play( - self.teacher.change, "raise_right_hand", - ) - self.change_student_modes( - "thinking", "pondering", "thinking", - look_at_arg=self.screen - ) - self.wait() - self.play(FadeInFromDown(khan_logo)) - self.look_at(self.screen) - self.wait(15) diff --git a/active_projects/ode/part2/shared_constructs.py b/active_projects/ode/part2/shared_constructs.py deleted file mode 100644 index 9418145e..00000000 --- a/active_projects/ode/part2/shared_constructs.py +++ /dev/null @@ -1,35 +0,0 @@ -from manimlib.imports import * - -TIME_COLOR = YELLOW -X_COLOR = GREEN - - -def get_heat_equation(): - pass - - -def temperature_to_color(temp, min_temp=-1, max_temp=1): - colors = [BLUE, TEAL, GREEN, YELLOW, "#ff0000"] - - alpha = inverse_interpolate(min_temp, max_temp, temp) - index, sub_alpha = integer_interpolate( - 0, len(colors) - 1, alpha - ) - return interpolate_color( - colors[index], colors[index + 1], sub_alpha - ) - - -def two_d_temp_func(x, y, t): - return np.sum([ - c * np.sin(f * var) * np.exp(-(f**2) * t) - for c, f, var in [ - (0.2, 1, x), - (0.3, 3, x), - (0.02, 5, x), - (0.01, 7, x), - (0.5, 2, y), - (0.1, 10, y), - (0.01, 20, y), - ] - ]) diff --git a/active_projects/ode/part2/staging.py b/active_projects/ode/part2/staging.py deleted file mode 100644 index 7450c823..00000000 --- a/active_projects/ode/part2/staging.py +++ /dev/null @@ -1,794 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part1.staging import TourOfDifferentialEquations - - -class PartTwoOfTour(TourOfDifferentialEquations): - CONFIG = { - "zoomed_thumbnail_index": 1, - } - - def construct(self): - self.add_title() - self.show_thumbnails() - self.zoom_in_to_one_thumbnail() - - def zoom_in_to_one_thumbnail(self): - frame = self.camera_frame - thumbnails = self.thumbnails - - ode = TextMobject("Ordinary\\\\", "Differential Equation") - pde = TextMobject("Partial\\\\", "Differential Equation") - for word, thumbnail, vect in zip([ode, pde], thumbnails, [DOWN, UP]): - word.match_width(thumbnail) - word.next_to(thumbnail, vect) - ode[0].set_color(BLUE) - pde[0].set_color(YELLOW) - - self.add(ode) - - frame.save_state() - self.play( - frame.replace, - thumbnails[0], - run_time=1, - ) - self.play( - Restore(frame, run_time=3), - ) - self.play( - TransformFromCopy(ode, pde), - ) - self.play( - ApplyMethod( - frame.replace, thumbnails[1], - path_arc=(-30 * DEGREES), - run_time=3 - ), - ) - self.wait() - - -class BrownianMotion(Scene): - CONFIG = { - "wait_time": 60, - "L": 3, # Box in [-L, L] x [-L, L] - "n_particles": 100, - "m1": 1, - "m2": 100, - "r1": 0.05, - "r2": 0.5, - "max_v": 5, - "random_seed": 2, - } - - def construct(self): - self.add_title() - self.add_particles() - self.wait(self.wait_time) - - def add_title(self): - square = Square(side_length=2 * self.L) - title = TextMobject("Brownian motion") - title.scale(1.5) - title.next_to(square, UP) - - self.add(square) - self.add(title) - - def add_particles(self): - m1 = self.m1 - m2 = self.m2 - r1 = self.r1 - r2 = self.r2 - L = self.L - max_v = self.max_v - n_particles = self.n_particles - - lil_particles = VGroup(*[ - self.get_particle(m1, r1, L, max_v) - for k in range(n_particles) - ]) - big_particle = self.get_particle(m2, r2, L=r2, max_v=0) - big_particle.set_fill(YELLOW, 1) - - for p in lil_particles: - if self.are_colliding(p, big_particle): - lil_particles.remove(p) - all_particles = VGroup(big_particle, *lil_particles) - all_particles.add_updater(self.update_particles) - - path = self.get_traced_path(big_particle) - - self.add(all_particles) - self.add(path) - - self.particles = all_particles - self.big_particle = big_particle - self.path = path - - def get_particle(self, m, r, L, max_v): - dot = Dot(radius=r) - dot.set_fill(WHITE, 0.7) - dot.mass = m - dot.radius = r - dot.center = op.add( - np.random.uniform(-L + r, L - r) * RIGHT, - np.random.uniform(-L + r, L - r) * UP - ) - dot.move_to(dot.center) - dot.velocity = rotate_vector( - np.random.uniform(0, max_v) * RIGHT, - np.random.uniform(0, TAU), - ) - return dot - - def are_colliding(self, p1, p2): - d = get_norm(p1.get_center() - p2.get_center()) - return (d < p1.radius + p2.radius) - - def get_traced_path(self, particle): - path = VMobject() - path.set_stroke(BLUE, 3) - path.start_new_path(particle.get_center()) - - buff = 0.02 - - def update_path(path): - new_point = particle.get_center() - if get_norm(new_point - path.get_last_point()) > buff: - path.add_line_to(new_point) - - path.add_updater(update_path) - return path - - def update_particles(self, particles, dt): - for p1 in particles: - p1.center += p1.velocity * dt - - # Check particle collisions - buff = 0.01 - for p2 in particles: - if p1 is p2: - continue - v = p2.center - p1.center - dist = get_norm(v) - r_sum = p1.radius + p2.radius - diff = dist - r_sum - if diff < 0: - unit_v = v / dist - p1.center += (diff - buff) * unit_v / 2 - p2.center += -(diff - buff) * unit_v / 2 - u1 = p1.velocity - u2 = p2.velocity - m1 = p1.mass - m2 = p2.mass - v1 = ( - (m2 * (u2 - u1) + m1 * u1 + m2 * u2) / - (m1 + m2) - ) - v2 = ( - (m1 * (u1 - u2) + m1 * u1 + m2 * u2) / - (m1 + m2) - ) - p1.velocity = v1 - p2.velocity = v2 - - # Check edge collisions - r1 = p1.radius - c1 = p1.center - for i in [0, 1]: - if abs(c1[i]) + r1 > self.L: - c1[i] = np.sign(c1[i]) * (self.L - r1) - p1.velocity[i] *= -1 * op.mul( - np.sign(p1.velocity[i]), - np.sign(c1[i]) - ) - - for p in particles: - p.move_to(p.center) - return particles - - -class AltBrownianMotion(BrownianMotion): - CONFIG = { - "wait_time": 20, - "n_particles": 100, - "m2": 10, - } - - -class BlackScholes(AltBrownianMotion): - def construct(self): - # For some reason I'm amused by the thought - # Of this graph perfectly matching the Brownian - # Motion y-coordiante - self.add_title() - self.add_particles() - self.particles.set_opacity(0) - self.remove(self.path) - self.add_graph() - self.wait(self.wait_time) - - def add_title(self): - title = TextMobject("Black-Sholes equations") - title.scale(1.5) - title.next_to(2 * UP, UP) - - equation = TexMobject( - "{\\partial V \\over \\partial t}", "+", - "\\frac{1}{2} \\sigma^2 S^2", - "{\\partial^2 V \\over \\partial S^2}", "+", - "rS", "{\\partial V \\over \\partial S}", - "-rV", "=", "0", - ) - equation.scale(0.8) - equation.next_to(title, DOWN) - - self.add(title) - self.add(equation) - self.title = title - self.equation = equation - - def add_graph(self): - axes = Axes( - x_min=-1, - x_max=20, - y_min=0, - y_max=10, - number_line_config={ - "unit_size": 0.5, - }, - ) - axes.set_height(4) - axes.move_to(DOWN) - - def get_graph_point(): - return axes.c2p( - self.get_time(), - 5 + 2 * self.big_particle.get_center()[1] - ) - - graph = VMobject() - graph.match_style(self.path) - graph.start_new_path(get_graph_point()) - graph.add_updater( - lambda g: g.add_line_to(get_graph_point()) - ) - - self.add(axes) - self.add(graph) - - -class ContrastChapters1And2(Scene): - def construct(self): - c1_frame, c2_frame = frames = VGroup(*[ - ScreenRectangle(height=3.5) - for x in range(2) - ]) - frames.arrange(RIGHT, buff=LARGE_BUFF) - - c1_title, c2_title = titles = VGroup( - TextMobject("Chapter 1"), - TextMobject("Chapter 2"), - ) - titles.scale(1.5) - - ode, pde = des = VGroup( - TextMobject( - "Ordinary", - "Differential Equations\\\\", - "ODEs", - ), - TextMobject( - "Partial", - "Differential Equations\\\\", - "PDEs", - ), - ) - ode[0].set_color(BLUE) - pde[0].set_color(YELLOW) - for de in des: - de[-1][0].match_color(de[0]) - de[-1].scale(1.5, about_point=de.get_top()) - - for title, frame, de in zip(titles, frames, des): - title.next_to(frame, UP) - de.match_width(frame) - de.next_to(frame, DOWN) - - lt = TexMobject("<") - lt.move_to(Line(ode.get_right(), pde.get_left())) - lt.scale(2, about_edge=UP) - - c1_words = TextMobject( - "They're", "really\\\\", "{}", - "freaking", "hard\\\\", - "to", "solve\\\\", - ) - c1_words.set_height(0.5 * c1_frame.get_height()) - c1_words.move_to(c1_frame) - - c2_words = TextMobject( - "They're", "really", "\\emph{really}\\\\", - "freaking", "hard\\\\", - "to", "solve\\\\", - ) - c2_words.set_color_by_tex("\\emph", YELLOW) - c2_words.move_to(c2_frame) - edit_shift = MED_LARGE_BUFF * RIGHT - c2_edits = VGroup( - TextMobject("sometimes").next_to( - c2_words[1:3], UP, - aligned_edge=LEFT, - ), - Line( - c2_words[1].get_left(), - c2_words[2].get_right(), - stroke_width=8, - ), - TextMobject("not too").next_to( - c2_words[3], LEFT, - ), - Line( - c2_words[3].get_left(), - c2_words[3].get_right(), - stroke_width=8, - ), - ) - c2_edits.set_color(RED) - c2_edits[2:].shift(edit_shift) - - self.add(titles) - self.add(frames) - self.add(des) - - self.wait() - self.play(LaggedStartMap( - FadeInFromDown, c1_words, - lag_ratio=0.1, - )) - self.wait() - # self.play(FadeIn(ode)) - self.play( - # TransformFromCopy(ode, pde), - TransformFromCopy(c1_words, c2_words), - Write(lt) - ) - self.wait() - self.play( - Write(c2_edits[:2], run_time=1), - ) - self.play( - c2_words[3:5].shift, edit_shift, - Write(c2_edits[2:]), - run_time=1, - ) - self.wait() - - -class ShowCubeFormation(ThreeDScene): - CONFIG = { - "camera_config": { - "shading_factor": 1.0, - }, - "color": False, - } - - def construct(self): - light_source = self.camera.light_source - light_source.move_to(np.array([-6, -3, 6])) - - cube = Cube( - side_length=4, - fill_color=GREY, - stroke_color=WHITE, - stroke_width=0.5, - ) - cube.set_fill(opacity=1) - if self.color: - # cube[0].set_color(BLUE) - # cube[1].set_color(RED) - # for face in cube[2:]: - # face.set_color([BLUE, RED]) - cube.color_using_background_image("VerticalTempGradient") - - # light_source.next_to(cube, np.array([1, -1, 1]), buff=2) - - cube_3d = cube.copy() - cube_2d = cube_3d.copy().stretch(0, 2) - cube_1d = cube_2d.copy().stretch(0, 1) - cube_0d = cube_1d.copy().stretch(0, 0) - - cube.become(cube_0d) - - self.set_camera_orientation( - phi=70 * DEGREES, - theta=-145 * DEGREES, - ) - self.begin_ambient_camera_rotation(rate=0.05) - - for target in [cube_1d, cube_2d, cube_3d]: - self.play( - Transform(cube, target, run_time=1.5) - ) - self.wait(8) - - -class ShowCubeFormationWithColor(ShowCubeFormation): - CONFIG = { - "color": True, - } - - -class ShowRect(Scene): - CONFIG = { - "height": 1, - "width": 3, - } - - def construct(self): - rect = Rectangle( - height=self.height, - width=self.width, - ) - rect.set_color(YELLOW) - self.play(ShowCreationThenFadeOut(rect)) - - -class ShowSquare(ShowRect): - CONFIG = { - "height": 1, - "width": 1, - } - - -class ShowHLine(Scene): - def construct(self): - line = Line(LEFT, RIGHT) - line.set_color(BLUE) - self.play(ShowCreationThenFadeOut(line)) - - -class ShowCross(Scene): - def construct(self): - cross = Cross(Square()) - cross.set_width(3) - cross.set_height(1, stretch=True) - self.play(ShowCreation(cross)) - - -class TwoBodyEquations(Scene): - def construct(self): - kw = { - "tex_to_color_map": { - "x_1": LIGHT_GREY, - "y_1": LIGHT_GREY, - "x_2": BLUE, - "y_2": BLUE, - "=": WHITE, - } - } - equations = VGroup( - TexMobject( - "{d^2 x_1 \\over dt^2}", - "=", - "{x_2 - x_1 \\over m_1 \\left(", - "(x_2 - x_1)^2 + (y_2 - y_1)^2", - "\\right)^{3/2}", - **kw - ), - TexMobject( - "{d^2 y_1 \\over dt^2}", - "=", - "{y_2 - y_1 \\over m_1 \\left(", - "(x_2 - x_1)^2 + (y_2 - y_1)^2", - "\\right)^{3/2}", - **kw - ), - TexMobject( - "{d^2 x_2 \\over dt^2}", - "=", - "{x_1 - x_2 \\over m_2 \\left(", - "(x_2 - x_1)^2 + (y_2 - y_1)^2", - "\\right)^{3/2}", - **kw - ), - TexMobject( - "{d^2 y_2 \\over dt^2}", - "=", - "{y_1 - y_2 \\over m_2 \\left(", - "(x_2 - x_1)^2 + (y_2 - y_1)^2", - "\\right)^{3/2}", - **kw - ), - ) - - equations.arrange(DOWN, buff=LARGE_BUFF) - equations.set_height(6) - equations.to_edge(LEFT) - - variables = VGroup() - lhss = VGroup() - rhss = VGroup() - for equation in equations: - variable = equation[1] - lhs = equation[:4] - rhs = equation[4:] - variables.add(variable) - lhss.add(lhs) - rhss.add(rhs) - lhss_copy = lhss.copy() - - for variable, lhs in zip(variables, lhss): - variable.save_state() - variable.match_height(lhs) - variable.scale(0.7) - variable.move_to(lhs, LEFT) - - self.play(LaggedStart(*[ - FadeInFrom(v, RIGHT) - for v in variables - ])) - self.wait() - self.play( - LaggedStartMap(Restore, variables), - FadeIn( - lhss_copy, - remover=True, - lag_ratio=0.1, - run_time=2, - ) - ) - self.add(lhss) - self.wait() - self.play(LaggedStartMap( - FadeIn, rhss - )) - self.wait() - self.play( - LaggedStart(*[ - ShowCreationThenFadeAround(lhs[:3]) - for lhs in lhss - ]) - ) - self.wait() - self.play( - LaggedStartMap( - ShowCreationThenFadeAround, - rhss, - ) - ) - self.wait() - - -class LaplacianIntuition(SpecialThreeDScene): - CONFIG = { - "three_d_axes_config": { - "x_min": -5, - "x_max": 5, - "y_min": -5, - "y_max": 5, - }, - "surface_resolution": 32, - } - - def construct(self): - axes = self.get_axes() - axes.scale(0.5, about_point=ORIGIN) - self.set_camera_to_default_position() - self.begin_ambient_camera_rotation() - - def func(x, y): - return np.array([ - x, y, - 2.7 + 0.5 * (np.sin(x) + np.cos(y)) - - 0.025 * (x**2 + y**2) - ]) - - surface_config = { - "u_min": -5, - "u_max": 5, - "v_min": -5, - "v_max": 5, - "resolution": self.surface_resolution, - } - # plane = ParametricSurface( - # lambda u, v: np.array([u, v, 0]), - # **surface_config - # ) - # plane.set_stroke(WHITE, width=0.1) - # plane.set_fill(WHITE, opacity=0.1) - plane = Square( - side_length=10, - stroke_width=0, - fill_color=WHITE, - fill_opacity=0.1, - ) - plane.center() - plane.set_shade_in_3d(True) - - surface = ParametricSurface( - func, **surface_config - ) - surface.set_stroke(BLUE, width=0.1) - surface.set_fill(BLUE, opacity=0.25) - - self.add(axes, plane, surface) - - point = VectorizedPoint(np.array([2, -2, 0])) - dot = Dot() - dot.set_color(GREEN) - dot.add_updater(lambda d: d.move_to(point)) - line = always_redraw(lambda: DashedLine( - point.get_location(), - func(*point.get_location()[:2]), - background_image_file="VerticalTempGradient", - )) - - circle = Circle(radius=0.25) - circle.set_color(YELLOW) - circle.insert_n_curves(20) - circle.add_updater(lambda m: m.move_to(point)) - circle.set_shade_in_3d(True) - surface_circle = always_redraw( - lambda: circle.copy().apply_function( - lambda p: func(*p[:2]) - ).shift( - 0.02 * IN - ).color_using_background_image("VerticalTempGradient") - ) - - self.play(FadeInFromLarge(dot)) - self.play(ShowCreation(line)) - self.play(TransformFromCopy(dot, circle)) - self.play( - Transform( - circle.copy(), - surface_circle.copy().clear_updaters(), - remover=True, - ) - ) - self.add(surface_circle) - - self.wait() - for vect in [4 * LEFT, DOWN, 4 * RIGHT, UP]: - self.play( - point.shift, vect, - run_time=3, - ) - - -class StrogatzMention(PiCreatureScene): - def construct(self): - self.show_book() - self.show_motives() - self.show_pages() - - def show_book(self): - morty = self.pi_creature - book = ImageMobject("InfinitePowers") - book.set_height(5) - book.to_edge(LEFT) - - steve = ImageMobject("Strogatz_by_bricks") - steve.set_height(5) - steve.to_edge(LEFT) - - name = TextMobject("Steven Strogatz") - name.match_width(steve) - name.next_to(steve, DOWN) - - self.think( - "Hmm...many good\\\\lessons here...", - run_time=1 - ) - self.wait() - self.play(FadeInFromDown(steve)) - self.wait() - self.play( - FadeInFrom(book, DOWN), - steve.shift, 4 * RIGHT, - RemovePiCreatureBubble( - morty, target_mode="thinking" - ) - ) - self.wait(3) - self.play( - FadeOut(steve), - FadeOut(morty), - ) - - self.book = book - - def show_motives(self): - motives = VGroup( - TextMobject("1) Scratch and itch"), - TextMobject("2) Make people love math"), - ) - motives.scale(1.5) - motives.arrange( - DOWN, LARGE_BUFF, - aligned_edge=LEFT, - ) - motives.move_to( - Line( - self.book.get_right(), - FRAME_WIDTH * RIGHT / 2 - ) - ) - motives.to_edge(UP) - - for motive in motives: - self.play(FadeInFromDown(motive)) - self.wait(2) - self.play(FadeOut(motives)) - - def show_pages(self): - book = self.book - pages = Group(*[ - ImageMobject("IP_Sample_Page{}".format(i)) - for i in range(1, 4) - ]) - for page in pages: - page.match_height(book) - page.next_to(book, RIGHT) - - last_page = VectorizedPoint() - for page in pages: - self.play( - FadeOut(last_page), - FadeIn(page) - ) - self.wait() - last_page = page - - self.play(FadeOut(last_page)) - - def create_pi_creature(self): - return Mortimer().to_corner(DR) - - -class Thumbnail(Scene): - def construct(self): - image = ImageMobject("HeatSurfaceExampleFlipped") - image.set_height(6.5) - image.to_edge(DOWN, buff=-SMALL_BUFF) - self.add(image) - - equation = TexMobject( - "{\\partial {T} \\over \\partial {t}}", "=", - "\\alpha", "\\nabla^2 {T}", - tex_to_color_map={ - "{t}": YELLOW, - "{T}": RED, - } - ) - equation.scale(2) - equation.to_edge(UP) - - self.add(equation) - - Group(equation, image).shift(1.5 * RIGHT) - - question = TextMobject("What is\\\\this?") - question.scale(2.5) - question.to_edge(LEFT) - arrow = Arrow( - question.get_top(), - equation.get_left(), - buff=0.5, - path_arc=-90 * DEGREES, - ) - arrow.set_stroke(width=5) - - self.add(question, arrow) - - -class ShowNewton(Scene): - def construct(self): - pass - - -class ShowCupOfWater(Scene): - def construct(self): - pass diff --git a/active_projects/ode/part2/wordy_scenes.py b/active_projects/ode/part2/wordy_scenes.py deleted file mode 100644 index 861474ae..00000000 --- a/active_projects/ode/part2/wordy_scenes.py +++ /dev/null @@ -1,785 +0,0 @@ -from manimlib.imports import * - - -class WriteHeatEquationTemplate(Scene): - CONFIG = { - "tex_mobject_config": { - "tex_to_color_map": { - "{T}": WHITE, - "{t}": YELLOW, - "{x}": GREEN, - "{y}": RED, - "{z}": BLUE, - "\\partial": WHITE, - "2": WHITE, - }, - }, - } - - def get_d1_equation(self): - return TexMobject( - "{\\partial {T} \\over \\partial {t}}({x}, {t})", "=", - "\\alpha \\cdot", - "{\\partial^2 {T} \\over \\partial {x}^2} ({x}, {t})", - **self.tex_mobject_config - ) - - def get_d1_equation_without_inputs(self): - return TexMobject( - "{\\partial {T} \\over \\partial {t}}", "=", - "\\alpha \\cdot", - "{\\partial^2 {T} \\over \\partial {x}^2}", - **self.tex_mobject_config - ) - - def get_d3_equation(self): - return TexMobject( - "{\\partial {T} \\over \\partial {t}}", "=", - "\\alpha \\left(", - "{\\partial^2 {T} \\over \\partial {x}^2} + ", - "{\\partial^2 {T} \\over \\partial {y}^2} + ", - "{\\partial^2 {T} \\over \\partial {z}^2}", - "\\right)", - **self.tex_mobject_config - ) - - def get_general_equation(self): - return TexMobject( - "{\\partial {T} \\over \\partial {t}}", "=", - "\\alpha", "\\nabla^2 {T}", - **self.tex_mobject_config, - ) - - def get_d3_equation_with_inputs(self): - return TexMobject( - "{\\partial {T} \\over \\partial {t}}", - "({x}, {y}, {z}, {t})", "=", - "\\alpha \\left(", - "{\\partial^2 {T} \\over \\partial {x}^2}", - "({x}, {y}, {z}, {t}) + ", - "{\\partial^2 {T} \\over \\partial {y}^2}", - "({x}, {y}, {z}, {t}) + ", - "{\\partial^2 {T} \\over \\partial {z}^2}", - "({x}, {y}, {z}, {t})", - "\\right)", - **self.tex_mobject_config - ) - - def get_d1_words(self): - return TextMobject("Heat equation\\\\", "(1 dimension)") - - def get_d3_words(self): - return TextMobject("Heat equation\\\\", "(3 dimensions)") - - def get_d1_group(self): - group = VGroup( - self.get_d1_words(), - self.get_d1_equation(), - ) - group.arrange(DOWN, buff=MED_LARGE_BUFF) - return group - - def get_d3_group(self): - group = VGroup( - self.get_d3_words(), - self.get_d3_equation(), - ) - group.arrange(DOWN, buff=MED_LARGE_BUFF) - return group - - -class HeatEquationIntroTitle(WriteHeatEquationTemplate): - def construct(self): - scale_factor = 1.25 - title = TextMobject("The Heat Equation") - title.scale(scale_factor) - title.to_edge(UP) - - equation = self.get_general_equation() - equation.scale(scale_factor) - equation.next_to(title, DOWN, MED_LARGE_BUFF) - equation.set_color_by_tex("{T}", RED) - - self.play( - FadeInFrom(title, DOWN), - FadeInFrom(equation, UP), - ) - self.wait() - - -class BringTogether(Scene): - def construct(self): - arrows = VGroup(Vector(2 * RIGHT), Vector(2 * LEFT)) - arrows.arrange(RIGHT, buff=2) - words = TextMobject("Bring together")[0] - words.next_to(arrows, DOWN) - words.save_state() - words.space_out_submobjects(1.2) - - self.play( - VFadeIn(words), - Restore(words), - arrows.arrange, RIGHT, {"buff": SMALL_BUFF}, - VFadeIn(arrows), - ) - self.play(FadeOut(words), FadeOut(arrows)) - - -class FourierSeriesIntro(WriteHeatEquationTemplate): - def construct(self): - title_scale_value = 1.5 - - title = TextMobject( - "Fourier ", "Series", - ) - title.scale(title_scale_value) - title.to_edge(UP) - title.generate_target() - - details_coming = TextMobject("Details coming...") - details_coming.next_to(title.get_corner(DR), DOWN) - details_coming.set_color(LIGHT_GREY) - - # physics = TextMobject("Physics") - heat = TextMobject("Heat") - heat.scale(title_scale_value) - physics = self.get_general_equation() - physics.set_color_by_tex("{T}", RED) - arrow1 = Arrow(LEFT, RIGHT) - arrow2 = Arrow(LEFT, RIGHT) - group = VGroup( - heat, arrow1, physics, arrow2, title.target - ) - group.arrange(RIGHT) - # physics.align_to(title.target, UP) - group.to_edge(UP) - - rot_square = Square() - rot_square.fade(1) - rot_square.add_updater(lambda m, dt: m.rotate(dt)) - - def update_heat_colors(heat): - colors = [YELLOW, RED] - vertices = rot_square.get_vertices() - letters = heat.family_members_with_points() - for letter, vertex in zip(letters, vertices): - alpha = (normalize(vertex)[0] + 1) / 2 - i, sa = integer_interpolate(0, len(colors) - 1, alpha) - letter.set_color(interpolate_color( - colors[i], colors[i + 1], alpha, - )) - heat.add_updater(update_heat_colors) - - image = ImageMobject("Joseph Fourier") - image.set_height(5) - image.next_to(title, DOWN, LARGE_BUFF) - image.to_edge(LEFT) - name = TextMobject("Joseph", "Fourier") - name.next_to(image, DOWN) - - bubble = ThoughtBubble( - height=2, - width=2.5, - direction=RIGHT, - ) - bubble.set_fill(opacity=0) - bubble.set_stroke(WHITE) - bubble.set_stroke(BLACK, 5, background=True) - bubble.shift(heat.get_center() - bubble.get_bubble_center()) - bubble[:-1].shift(LEFT + 0.2 * DOWN) - bubble[:-1].rotate(-20 * DEGREES) - for mob in bubble[:-1]: - mob.rotate(20 * DEGREES) - - # self.play(FadeInFromDown(title)) - self.add(title) - self.play( - FadeInFromDown(image), - TransformFromCopy( - title.get_part_by_tex("Fourier"), - name.get_part_by_tex("Fourier"), - path_arc=90 * DEGREES, - ), - FadeIn(name.get_part_by_tex("Joseph")), - ) - self.play(Write(details_coming, run_time=1)) - self.play(LaggedStartMap(FadeOut, details_coming[0], run_time=1)) - self.wait() - self.add(rot_square) - self.play( - FadeInFrom(physics, RIGHT), - GrowArrow(arrow2), - FadeInFrom(heat, RIGHT), - GrowArrow(arrow1), - MoveToTarget(title), - ) - self.play(ShowCreation(bubble)) - self.wait(10) - - -class CompareODEToPDE(Scene): - def construct(self): - pass - - -class TodaysTargetWrapper(Scene): - def construct(self): - pass - - -class TwoGraphTypeTitles(Scene): - def construct(self): - left_title = TextMobject( - "Represent time\\\\with actual time" - ) - left_title.shift(FRAME_WIDTH * LEFT / 4) - right_title = TextMobject( - "Represent time\\\\with an axis" - ) - right_title.shift(FRAME_WIDTH * RIGHT / 4) - - titles = VGroup(left_title, right_title) - for title in titles: - title.scale(1.25) - title.to_edge(UP) - - self.play(FadeInFromDown(right_title)) - self.wait() - self.play(FadeInFromDown(left_title)) - self.wait() - - -class ShowPartialDerivativeSymbols(Scene): - def construct(self): - t2c = { - "{x}": GREEN, - "{t}": YELLOW, - } - d_derivs, del_derivs = VGroup(*[ - VGroup(*[ - TexMobject( - "{" + sym, "T", "\\over", sym, var + "}", - "(", "{x}", ",", "{t}", ")", - ).set_color_by_tex_to_color_map(t2c) - for var in ("{x}", "{t}") - ]) - for sym in ("d", "\\partial") - ]) - dTdx, dTdt = d_derivs - delTdelx, delTdelx = del_derivs - dels = VGroup(*it.chain(*[ - del_deriv.get_parts_by_tex("\\partial") - for del_deriv in del_derivs - ])) - - dTdx.to_edge(UP) - self.play(FadeInFrom(dTdx, DOWN)) - self.wait() - self.play(ShowCreationThenFadeAround(dTdx[3:5])) - self.play(ShowCreationThenFadeAround(dTdx[:2])) - self.wait() - - dTdt.move_to(dTdx) - self.play( - dTdx.next_to, dTdt, RIGHT, {"buff": 1.5}, - dTdx.set_opacity, 0.5, - FadeInFromDown(dTdt) - ) - self.wait() - - for m1, m2 in zip(d_derivs, del_derivs): - m2.move_to(m1) - - pd_words = TextMobject("Partial derivatives") - pd_words.next_to(del_derivs, DOWN, MED_LARGE_BUFF) - - self.play( - Write(pd_words), - dTdx.set_opacity, 1, - run_time=1, - ) - self.wait() - self.play( - ReplacementTransform(d_derivs, del_derivs) - ) - self.play( - LaggedStartMap( - ShowCreationThenFadeAround, - dels, - surrounding_rectangle_config={ - "color": BLUE, - "buff": 0.5 * SMALL_BUFF, - "stroke_width": 2, - } - ) - ) - self.wait() - - num_words = VGroup(*[ - TextMobject( - "Change in $T$\\\\caused by {}", - "$\\partial$", "${}$".format(var), - arg_separator="", - ).set_color_by_tex_to_color_map(t2c) - for var in ("{x}", "{t}") - ]) - num_words.scale(0.8) - for word, deriv in zip(num_words, del_derivs): - num = deriv[:2] - word.move_to(num, UP) - word.to_edge(UP, buff=MED_SMALL_BUFF) - deriv.rect = SurroundingRectangle( - num, - buff=SMALL_BUFF, - stroke_width=2, - color=word[-1].get_color(), - ) - deriv.rect.mob = num - deriv.rect.add_updater(lambda r: r.move_to(r.mob)) - - self.play( - Write(num_words[1]), - VGroup(del_derivs, pd_words).shift, DOWN, - ShowCreation(del_derivs[1].rect), - ) - self.play( - Write(num_words[0]), - ShowCreation(del_derivs[0].rect), - ) - self.wait() - - -class WriteHeatEquation(WriteHeatEquationTemplate): - def construct(self): - title = TextMobject("The Heat Equation") - title.to_edge(UP) - - equation = self.get_d1_equation() - equation.next_to(title, DOWN) - - eq_i = equation.index_of_part_by_tex("=") - dt_part = equation[:eq_i] - dx_part = equation[eq_i + 3:] - dt_rect = SurroundingRectangle(dt_part) - dt_rect.set_stroke(YELLOW, 2) - dx_rect = SurroundingRectangle(dx_part) - dx_rect.set_stroke(GREEN, 2) - - two_outlines = equation.get_parts_by_tex("2").copy() - two_outlines.set_stroke(YELLOW, 2) - two_outlines.set_fill(opacity=0) - - to_be_explained = TextMobject( - "To be explained shortly..." - ) - to_be_explained.scale(0.7) - to_be_explained.next_to(equation, RIGHT, MED_LARGE_BUFF) - to_be_explained.fade(1) - - pde = TextMobject("Partial Differential Equation") - pde.move_to(title) - - del_outlines = equation.get_parts_by_tex("\\partial").copy() - del_outlines.set_stroke(YELLOW, 2) - del_outlines.set_fill(opacity=0) - - self.play( - FadeInFrom(title, 0.5 * DOWN), - FadeInFrom(equation, 0.5 * UP), - ) - self.wait() - self.play(ShowCreation(dt_rect)) - self.wait() - self.play(TransformFromCopy(dt_rect, dx_rect)) - self.play(ShowCreationThenDestruction(two_outlines)) - self.wait() - self.play(Write(to_be_explained, run_time=1)) - self.wait(2) - self.play( - ShowCreationThenDestruction( - del_outlines, - lag_ratio=0.1, - ) - ) - self.play( - FadeOutAndShift(title, UP), - FadeInFrom(pde, DOWN), - FadeOut(dt_rect), - FadeOut(dx_rect), - ) - self.wait() - - -class Show3DEquation(WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d3_equation_with_inputs() - equation.set_width(FRAME_WIDTH - 1) - inputs = VGroup(*it.chain(*[ - equation.get_parts_by_tex(s) - for s in ["{x}", "{y}", "{z}", "{t}"] - ])) - inputs.sort() - equation.to_edge(UP) - - self.add(equation) - self.play(LaggedStartMap( - ShowCreationThenFadeAround, inputs, - surrounding_rectangle_config={ - "buff": 0.05, - "stroke_width": 2, - } - )) - self.wait() - - -class Show1DAnd3DEquations(WriteHeatEquationTemplate): - def construct(self): - d1_group = self.get_d1_group() - d3_group = self.get_d3_group() - d1_words, d1_equation = d1_group - d3_words, d3_equation = d3_group - - groups = VGroup(d1_group, d3_group) - for group in groups: - group.arrange(DOWN, buff=MED_LARGE_BUFF) - groups.arrange(RIGHT, buff=1.5) - groups.to_edge(UP) - - d3_rhs = d3_equation[9:-2] - d3_brace = Brace(d3_rhs, DOWN) - nabla_words = TextMobject("Sometimes written as") - nabla_words.match_width(d3_brace) - nabla_words.next_to(d3_brace, DOWN) - nabla_exp = TexMobject( - "\\nabla^2 {T}", - **self.tex_mobject_config, - ) - nabla_exp.next_to(nabla_words, DOWN) - # nabla_group = VGroup(nabla_words, nabla_exp) - - d1_group.save_state() - d1_group.center().to_edge(UP) - - self.play( - Write(d1_words), - FadeInFrom(d1_equation, UP), - run_time=1, - ) - self.wait(2) - self.play( - Restore(d1_group), - FadeInFrom(d3_group, LEFT) - ) - self.wait() - self.play( - GrowFromCenter(d3_brace), - Write(nabla_words), - TransformFromCopy(d3_rhs, nabla_exp), - run_time=1, - ) - self.wait() - - -class D1EquationNoInputs(WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d1_equation_without_inputs() - equation.to_edge(UP) - # i1 = equation.index_of_part_by_tex("\\partial") - # i2 = equation.index_of_part_by_tex("\\cdot") - # equation[i1:i1 + 2].set_color(RED) - # equation[i2 + 1:i2 + 6].set_color(RED) - equation.set_color_by_tex("{T}", RED) - self.add(equation) - - -class AltHeatRHS(Scene): - def construct(self): - formula = TexMobject( - "{\\alpha \\over 2}", "\\Big(", - "T({x} - 1, {t}) + T({x} + 1, {t})" - "\\Big)", - tex_to_color_map={ - "{x}": GREEN, - "{t}": YELLOW, - } - ) - self.add(formula) - - -class CompareInputsOfGeneralCaseTo1D(WriteHeatEquation): - def construct(self): - three_d_expr, one_d_expr = [ - TexMobject( - "{T}(" + inputs + ", {t})", - **self.tex_mobject_config, - ) - for inputs in ["{x}, {y}, {z}", "{x}"] - ] - for expr in three_d_expr, one_d_expr: - expr.scale(2) - expr.to_edge(UP) - - x, y, z = [ - three_d_expr.get_part_by_tex(letter) - for letter in ["x", "y", "z"] - ] - - self.play(FadeInFromDown(three_d_expr)) - self.play(LaggedStartMap( - ShowCreationThenFadeAround, - VGroup(x, y, z) - )) - self.wait() - low = 3 - high = -3 - self.play( - ReplacementTransform(three_d_expr[:low], one_d_expr[:low]), - ReplacementTransform(three_d_expr[high:], one_d_expr[high:]), - three_d_expr[low:high].scale, 0, - ) - self.wait() - - -class ShowLaplacian(WriteHeatEquation): - def construct(self): - equation = self.get_d3_equation() - equation.to_edge(UP, buff=MED_SMALL_BUFF) - - parts = VGroup() - plusses = VGroup() - for char in "xyz": - index = equation.index_of_part_by_tex( - "{" + char + "}" - ) - part = equation[index - 6:index + 3] - rect = SurroundingRectangle(part) - rect.match_color(equation[index]) - parts.add(part) - part.rect = rect - if char in "yz": - plus = equation[index - 8] - part.plus = plus - plusses.add(plus) - - lp = equation.get_part_by_tex("(") - rp = equation.get_part_by_tex(")") - - for part in parts: - part.rp = rp.copy() - part.rp.next_to(part, RIGHT, SMALL_BUFF) - part.rp.align_to(lp, UP) - rp.become(parts[0].rp) - - # Show new second derivatives - self.add(*equation) - self.remove(*plusses, *parts[1], *parts[2]) - for part in parts[1:]: - self.play( - rp.become, part.rp, - FadeInFrom(part, LEFT), - Write(part.plus), - ShowCreation(part.rect), - ) - self.play( - FadeOut(part.rect), - ) - self.wait() - - # Show laplacian - brace = Brace(parts, DOWN) - laplacian = TexMobject("\\nabla^2", "T") - laplacian.next_to(brace, DOWN) - laplacian_name = TextMobject( - "``Laplacian''" - ) - laplacian_name.next_to(laplacian, DOWN) - - T_parts = VGroup(*[part[3] for part in parts]) - non_T_parts = VGroup(*[ - VGroup(*part[:3], *part[4:]) - for part in parts - ]) - - self.play(GrowFromCenter(brace)) - self.play(Write(laplacian_name)) - self.play( - TransformFromCopy(non_T_parts, laplacian[0]) - ) - self.play( - TransformFromCopy(T_parts, laplacian[1]) - ) - self.wait(3) - - -class AskAboutActuallySolving(WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d1_equation() - equation.center() - - q1 = TextMobject("Solve for T?") - q1.next_to(equation, UP, LARGE_BUFF) - q2 = TextMobject("What does it \\emph{mean} to solve this?") - q2.next_to(equation, UP, LARGE_BUFF) - formula = TexMobject( - "T({x}, {t}) = \\sin\\big(a{x}\\big) e^{-\\alpha \\cdot a^2 {t}}", - tex_to_color_map={ - "{x}": GREEN, - "{t}": YELLOW, - } - ) - formula.next_to(equation, DOWN, LARGE_BUFF) - q3 = TextMobject("Is this it?") - arrow = Vector(LEFT, color=WHITE) - arrow.next_to(formula, RIGHT) - q3.next_to(arrow, RIGHT) - - self.add(equation) - self.play(FadeInFromDown(q1)) - self.wait() - self.play( - FadeInFromDown(q2), - q1.shift, 1.5 * UP, - ) - self.play(FadeInFrom(formula, UP)) - self.play( - GrowArrow(arrow), - FadeInFrom(q3, LEFT) - ) - self.wait() - - -class PDEPatreonEndscreen(PatreonEndScreen): - CONFIG = { - "specific_patrons": [ - "Juan Benet", - "Vassili Philippov", - "Burt Humburg", - "Matt Russell", - "Scott Gray", - "soekul", - "Tihan Seale", - "Richard Barthel", - "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", - "D. Sivakumar", - "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", - "Antoine Bruguier", - "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 B. Hill", - "David Clark", - "DeathByShrimp", - "Delton Ding", - "eaglle", - "emptymachine", - "Eric Younge", - "Eryq Ouithaqueue", - "Federico Lebron", - "Giovanni Filippi", - "Hal Hildebrand", - "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 Beck", - "Lee Redden", - "Linh Tran", - "Ludwig Schubert", - "Magister Mugit", - "Mark B Bahu", - "Mark Heising", - "Martin Price", - "Mathias Jansson", - "Matt Langford", - "Matt Roveto", - "Matthew Bouchard", - "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", - "Sachit Nagpal", - "Solara570", - "Stevie Metke", - "Tal Einav", - "Ted Suzman", - "Thomas Tarler", - "Tom Fleming", - "Valeriy Skobelev", - "Xavier Bernard", - "Yavor Ivanov", - "Yaw Etse", - "YinYangBalance.Asia", - "Zach Cardwell", - ], - } diff --git a/active_projects/ode/part3/discrete_case.py b/active_projects/ode/part3/discrete_case.py deleted file mode 100644 index 2c8ae08c..00000000 --- a/active_projects/ode/part3/discrete_case.py +++ /dev/null @@ -1,303 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part2.heat_equation import * - - -class ShowNewRuleAtDiscreteBoundary(DiscreteSetup): - CONFIG = { - "axes_config": { - "x_min": 0, - "stroke_width": 1, - "x_axis_config": { - "include_tip": False, - }, - }, - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - ], - "v_line_class": DashedLine, - "v_line_config": { - - }, - "step_size": 1, - "wait_time": 15, - "alpha": 0.25, - } - - def construct(self): - self.add_axes() - self.set_points() - self.show_boundary_point_influenced_by_neighbor() - self.add_clock() - self.let_evolve() - - def set_points(self): - axes = self.axes - for mob in axes.family_members_with_points(): - if isinstance(mob, Line): - mob.set_stroke(width=1) - - step_size = self.step_size - xs = np.arange( - axes.x_min, - axes.x_max + step_size, - step_size - ) - - dots = self.dots = self.get_dots(axes, xs) - self.v_lines = self.get_v_lines(dots) - self.rod_pieces = self.get_rod_pieces(dots) - - # rod_pieces - - self.add(self.dots) - self.add(self.v_lines) - self.add(self.rod_pieces) - - def show_boundary_point_influenced_by_neighbor(self): - dots = self.dots - ld = dots[0] - ld_in = dots[1] - rd = dots[-1] - rd_in = dots[-2] - v_len = 0.75 - l_arrow = Vector(v_len * LEFT) - l_arrow.move_to(ld.get_left(), RIGHT) - r_arrow = Vector(v_len * RIGHT) - r_arrow.move_to(rd.get_right(), LEFT) - arrows = VGroup(l_arrow, r_arrow) - q_marks = VGroup(*[ - TexMobject("?").scale(1.5).next_to( - arrow, arrow.get_vector() - ) - for arrow in arrows - ]) - - arrows.set_color(YELLOW) - q_marks.set_color(YELLOW) - - blocking_rects = VGroup(*[ - BackgroundRectangle(VGroup( - *dots[i:-i], - *self.rod_pieces[i:-i] - )) - for i in [1, 2] - ]) - for rect in blocking_rects: - rect.stretch(1.1, dim=1, about_edge=UP) - - self.play(FadeIn(blocking_rects[0])) - self.play( - LaggedStartMap(ShowCreation, arrows), - LaggedStart(*[ - FadeInFrom(q_mark, -arrow.get_vector()) - for q_mark, arrow in zip(q_marks, arrows) - ]), - run_time=1.5 - ) - self.wait() - - # Point to inward neighbor - new_arrows = VGroup(*[ - Arrow( - d1.get_center(), - VGroup(d1, d2).get_center(), - buff=0, - ).match_style(l_arrow) - for d1, d2 in [(ld, ld_in), (rd, rd_in)] - ]) - new_arrows.match_style(arrows) - - l_brace = Brace(VGroup(ld, ld_in), DOWN) - r_brace = Brace(VGroup(rd, rd_in), DOWN) - braces = VGroup(l_brace, r_brace) - for brace in braces: - brace.align_to( - self.axes.x_axis.get_center(), UP - ) - brace.shift(SMALL_BUFF * DOWN) - brace.add(brace.get_tex("\\Delta x")) - - self.play( - ReplacementTransform(arrows, new_arrows), - FadeOut(q_marks), - ReplacementTransform(*blocking_rects) - ) - self.wait() - self.play(FadeInFrom(braces, UP)) - self.wait() - self.play( - FadeOut(new_arrows), - FadeOut(blocking_rects[1]), - FadeOut(braces), - ) - - def add_clock(self): - super().add_clock() - self.time_label.add_updater( - lambda d, dt: d.increment_value(dt) - ) - VGroup( - self.clock, - self.time_label - ).shift(2 * LEFT) - - def let_evolve(self): - dots = self.dots - dots.add_updater(self.update_dots) - - wait_time = self.wait_time - self.play( - ClockPassesTime( - self.clock, - run_time=wait_time, - hours_passed=wait_time, - ), - ) - - # - - def get_dots(self, axes, xs): - dots = VGroup(*[ - Dot(axes.c2p(x, self.temp_func(x, 0))) - for x in xs - ]) - - max_width = 0.8 * self.step_size - for dot in dots: - dot.add_updater(self.update_dot_color) - if dot.get_width() > max_width: - dot.set_width(max_width) - - return dots - - def get_v_lines(self, dots): - return always_redraw(lambda: VGroup(*[ - self.get_v_line(dot) - for dot in dots - ])) - - def get_v_line(self, dot): - x_axis = self.axes.x_axis - bottom = dot.get_bottom() - x = x_axis.p2n(bottom) - proj_point = x_axis.n2p(x) - return self.v_line_class( - proj_point, bottom, - **self.v_line_config, - ) - - def get_rod_pieces(self, dots): - axis = self.axes.x_axis - factor = 1 - np.exp(-(0.8 / self.step_size)**2) - width = factor * self.step_size - - pieces = VGroup() - for dot in dots: - piece = Line(ORIGIN, width * RIGHT) - piece.set_stroke(width=5) - piece.move_to(dot) - piece.set_y(axis.get_center()[1]) - piece.dot = dot - piece.add_updater( - lambda p: p.match_color(p.dot) - ) - pieces.add(piece) - return pieces - - def update_dot_color(self, dot): - y = self.axes.y_axis.p2n(dot.get_center()) - dot.set_color(self.y_to_color(y)) - - def update_dots(self, dots, dt): - for ds in zip(dots, dots[1:], dots[2:]): - points = [d.get_center() for d in ds] - x0, x1, x2 = [p[0] for p in points] - dx = x1 - x0 - y0, y1, y2 = [p[1] for p in points] - - self.update_dot( - dot=ds[1], - dt=dt, - mean_diff=0.5 * (y2 - 2 * y1 + y0) / dx - ) - if ds[0] is dots[0]: - self.update_dot( - dot=ds[0], - dt=dt, - mean_diff=(y1 - y0) / dx - ) - elif ds[-1] is dots[-1]: - self.update_dot( - dot=ds[-1], - dt=dt, - mean_diff=(y1 - y2) / dx - ) - - def update_dot(self, dot, dt, mean_diff): - dot.shift(mean_diff * self.alpha * dt * UP) - - -class DiscreteEvolutionPoint25(ShowNewRuleAtDiscreteBoundary): - CONFIG = { - "step_size": 0.25, - "alpha": 0.5, - "wait_time": 30, - } - - def construct(self): - self.add_axes() - self.set_points() - self.add_clock() - self.let_evolve() - - -class DiscreteEvolutionPoint1(DiscreteEvolutionPoint25): - CONFIG = { - "step_size": 0.1, - "v_line_config": { - "stroke_width": 1, - }, - "wait_time": 30, - } - - -class FlatEdgesForDiscreteEvolution(DiscreteEvolutionPoint1): - CONFIG = { - "wait_time": 20, - "step_size": 0.1, - } - - def let_evolve(self): - lines = VGroup(*[ - Line(LEFT, RIGHT) - for x in range(2) - ]) - lines.set_width(1.5) - lines.set_stroke(WHITE, 5, opacity=0.5) - lines.add_updater(self.update_lines) - - turn_animation_into_updater( - ShowCreation(lines, run_time=2) - ) - self.add(lines) - - super().let_evolve() - - def update_lines(self, lines): - dots = self.dots - for line, dot in zip(lines, [dots[0], dots[-1]]): - line.move_to(dot) - - -class FlatEdgesForDiscreteEvolutionTinySteps(FlatEdgesForDiscreteEvolution): - CONFIG = { - "step_size": 0.025, - "wait_time": 10, - "v_line_class": Line, - "v_line_config": { - "stroke_opacity": 0.5, - } - } diff --git a/active_projects/ode/part3/pi_creature_scenes.py b/active_projects/ode/part3/pi_creature_scenes.py deleted file mode 100644 index 61b8f7c0..00000000 --- a/active_projects/ode/part3/pi_creature_scenes.py +++ /dev/null @@ -1,175 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part2.wordy_scenes import * - - -class IveHeardOfThis(TeacherStudentsScene): - def construct(self): - point = VectorizedPoint() - point.move_to(3 * RIGHT + 2 * UP) - self.student_says( - "I've heard\\\\", "of this!", - student_index=1, - target_mode="hooray", - bubble_kwargs={ - "height": 3, - "width": 3, - "direction": RIGHT, - }, - run_time=1, - ) - self.change_student_modes( - "thinking", "hooray", "thinking", - look_at_arg=point, - added_anims=[self.teacher.change, "happy"] - ) - self.wait(3) - self.student_says( - "But who\\\\", "cares?", - student_index=1, - target_mode="maybe", - bubble_kwargs={ - "direction": RIGHT, - "width": 3, - "height": 3, - }, - run_time=1, - ) - self.change_student_modes( - "pondering", "maybe", "pondering", - look_at_arg=point, - added_anims=[self.teacher.change, "guilty"] - ) - self.wait(5) - - -class InFouriersShoes(PiCreatureScene, WriteHeatEquationTemplate): - def construct(self): - randy = self.pi_creature - fourier = ImageMobject("Joseph Fourier") - fourier.set_height(4) - fourier.next_to(randy, RIGHT, LARGE_BUFF) - fourier.align_to(randy, DOWN) - - equation = self.get_d1_equation() - equation.next_to(fourier, UP, MED_LARGE_BUFF) - - decades = list(range(1740, 2040, 20)) - time_line = NumberLine( - x_min=decades[0], - x_max=decades[-1], - tick_frequency=1, - tick_size=0.05, - longer_tick_multiple=4, - unit_size=0.2, - numbers_with_elongated_ticks=decades, - numbers_to_show=decades, - decimal_number_config={ - "group_with_commas": False, - }, - stroke_width=2, - ) - time_line.add_numbers() - time_line.move_to(ORIGIN, RIGHT) - time_line.to_edge(UP) - triangle = ArrowTip(start_angle=-90 * DEGREES) - triangle.set_height(0.25) - triangle.move_to(time_line.n2p(2019), DOWN) - triangle.set_color(WHITE) - - self.play(FadeInFrom(fourier, 2 * LEFT)) - self.play(randy.change, "pondering") - self.wait() - self.play( - DrawBorderThenFill(triangle, run_time=1), - FadeInFromDown(equation), - FadeIn(time_line), - ) - self.play( - Animation(triangle), - ApplyMethod( - time_line.shift, - time_line.n2p(2019) - time_line.n2p(1822), - run_time=5 - ), - ) - self.wait() - - -class SineCurveIsUnrealistic(TeacherStudentsScene): - def construct(self): - self.student_says( - "But that would\\\\never happen!", - student_index=1, - bubble_kwargs={ - "direction": RIGHT, - "height": 3, - "width": 4, - }, - target_mode="angry" - ) - self.change_student_modes( - "guilty", "angry", "hesitant", - added_anims=[ - self.teacher.change, "tease" - ] - ) - self.wait(3) - self.play( - RemovePiCreatureBubble(self.students[1]), - self.teacher.change, "raise_right_hand" - ) - self.change_all_student_modes( - "pondering", - look_at_arg=3 * UP, - ) - self.wait(5) - - -class IfOnly(TeacherStudentsScene): - def construct(self): - self.teacher_says( - "If only!", - target_mode="angry" - ) - self.change_all_student_modes( - "confused", - look_at_arg=self.screen - ) - self.wait(3) - - -class SoWeGotNowhere(TeacherStudentsScene): - def construct(self): - self.student_says( - "So we've gotten\\\\nowhere!", - target_mode="angry", - added_anims=[ - self.teacher.change, "guilty" - ] - ) - self.change_all_student_modes("angry") - self.wait() - text = TexMobject( - "&\\text{Actually,}\\\\", - "&\\sin\\left({x}\\right)" - "e^{-\\alpha {t}}\\\\", - "&\\text{isn't far off.}", - tex_to_color_map={ - "{x}": GREEN, - "{t}": YELLOW, - } - ) - text.scale(0.8) - self.teacher_says( - text, - content_introduction_class=FadeIn, - bubble_kwargs={ - "width": 4, - "height": 3.5, - } - ) - self.change_all_student_modes( - "pondering", - look_at_arg=self.screen - ) - self.wait(3) diff --git a/active_projects/ode/part3/staging.py b/active_projects/ode/part3/staging.py deleted file mode 100644 index e7cc4871..00000000 --- a/active_projects/ode/part3/staging.py +++ /dev/null @@ -1,1230 +0,0 @@ -from manimlib.imports import * - -from active_projects.ode.part2.fourier_series import FourierOfTrebleClef - - -class FourierNameIntro(Scene): - def construct(self): - self.show_two_titles() - self.transition_to_image() - self.show_paper() - - def show_two_titles(self): - lt = TextMobject("Fourier", "Series") - rt = TextMobject("Fourier", "Transform") - lt_variants = VGroup( - TextMobject("Complex", "Fourier Series"), - TextMobject("Discrete", "Fourier Series"), - ) - rt_variants = VGroup( - TextMobject("Discrete", "Fourier Transform"), - TextMobject("Fast", "Fourier Transform"), - TextMobject("Quantum", "Fourier Transform"), - ) - - titles = VGroup(lt, rt) - titles.scale(1.5) - for title, vect in (lt, LEFT), (rt, RIGHT): - title.move_to(vect * FRAME_WIDTH / 4) - title.to_edge(UP) - - for title, variants in (lt, lt_variants), (rt, rt_variants): - title.save_state() - title.target = title.copy() - title.target.scale(1 / 1.5, about_edge=RIGHT) - for variant in variants: - variant.move_to(title.target, UR) - variant[0].set_color(YELLOW) - - v_line = Line(UP, DOWN) - v_line.set_height(FRAME_HEIGHT) - v_line.set_stroke(WHITE, 2) - - self.play( - FadeInFrom(lt, RIGHT), - ShowCreation(v_line) - ) - self.play( - FadeInFrom(rt, LEFT), - ) - # Edit in images of circle animations - # and clips from FT video - - # for title, variants in (rt, rt_variants), (lt, lt_variants): - for title, variants in [(rt, rt_variants)]: - # Maybe do it for left variant, maybe not... - self.play( - MoveToTarget(title), - FadeInFrom(variants[0][0], LEFT) - ) - for v1, v2 in zip(variants, variants[1:]): - self.play( - FadeOutAndShift(v1[0], UP), - FadeInFrom(v2[0], DOWN), - run_time=0.5, - ) - self.wait(0.5) - self.play( - Restore(title), - FadeOut(variants[-1][0]) - ) - self.wait() - - self.titles = titles - self.v_line = v_line - - def transition_to_image(self): - titles = self.titles - v_line = self.v_line - - image = ImageMobject("Joseph Fourier") - image.set_height(5) - image.to_edge(LEFT) - - frame = Rectangle() - frame.replace(image, stretch=True) - - name = TextMobject("Joseph", "Fourier") - fourier_part = name.get_part_by_tex("Fourier") - fourier_part.set_color(YELLOW) - F_sym = fourier_part[0] - name.match_width(image) - name.next_to(image, DOWN) - - self.play( - ReplacementTransform(v_line, frame), - FadeIn(image), - FadeIn(name[0]), - *[ - ReplacementTransform( - title[0].deepcopy(), - name[1] - ) - for title in titles - ], - titles.scale, 0.65, - titles.arrange, DOWN, - titles.next_to, image, UP, - ) - self.wait() - - big_F = F_sym.copy() - big_F.set_fill(opacity=0) - big_F.set_stroke(WHITE, 2) - big_F.set_height(3) - big_F.move_to(midpoint( - image.get_right(), - RIGHT_SIDE, - )) - big_F.shift(DOWN) - equivalence = VGroup( - fourier_part.copy().scale(1.25), - TexMobject("\\Leftrightarrow").scale(1.5), - TextMobject("Break down into\\\\pure frequencies"), - ) - equivalence.arrange(RIGHT) - equivalence.move_to(big_F) - equivalence.to_edge(UP) - - self.play( - FadeIn(big_F), - TransformFromCopy(fourier_part, equivalence[0]), - Write(equivalence[1:]), - ) - self.wait(3) - self.play(FadeOut(VGroup(big_F, equivalence))) - - self.image = image - self.name = name - - def show_paper(self): - image = self.image - paper = ImageMobject("Fourier paper") - paper.match_height(image) - paper.next_to(image, RIGHT, MED_LARGE_BUFF) - - date = TexMobject("1822") - date.next_to(paper, DOWN) - date_rect = SurroundingRectangle(date) - date_rect.scale(0.3) - date_rect.set_color(RED) - date_rect.shift(1.37 * UP + 0.08 * LEFT) - date_arrow = Arrow( - date_rect.get_bottom(), - date.get_top(), - buff=SMALL_BUFF, - color=date_rect.get_color(), - ) - - heat_rect = SurroundingRectangle( - TextMobject("CHALEUR") - ) - heat_rect.set_color(RED) - heat_rect.scale(0.6) - heat_rect.move_to( - paper.get_top() + - 1.22 * DOWN + 0.37 * RIGHT - ) - heat_word = TextMobject("Heat") - heat_word.scale(1.5) - heat_word.next_to(paper, UP) - heat_word.shift(paper.get_width() * RIGHT) - heat_arrow = Arrow( - heat_rect.get_top(), - heat_word.get_left(), - buff=0.1, - path_arc=-60 * DEGREES, - color=heat_rect.get_color(), - ) - - self.play(FadeInFrom(paper, LEFT)) - self.play( - ShowCreation(date_rect), - ) - self.play( - GrowFromPoint(date, date_arrow.get_start()), - ShowCreation(date_arrow), - ) - self.wait(3) - - # Insert animation of circles/sine waves - # approximating a square wave - - self.play( - ShowCreation(heat_rect), - ) - self.play( - GrowFromPoint(heat_word, heat_arrow.get_start()), - ShowCreation(heat_arrow), - ) - self.wait(3) - - -class ManyCousinsOfFourierThings(Scene): - def construct(self): - series_variants = VGroup( - TextMobject("Complex", "Fourier Series"), - TextMobject("Discrete", "Fourier Series"), - ) - transform_variants = VGroup( - TextMobject("Discrete", "Fourier Transform"), - TextMobject("Fast", "Fourier Transform"), - TextMobject("Quantum", "Fourier Transform"), - ) - groups = VGroup(series_variants, transform_variants) - for group, vect in zip(groups, [LEFT, RIGHT]): - group.scale(0.7) - group.arrange(DOWN, aligned_edge=LEFT) - group.move_to( - vect * FRAME_WIDTH / 4 - ) - group.set_color(YELLOW) - - self.play(*[ - LaggedStartMap(FadeIn, group) - for group in groups - ]) - self.play(*[ - LaggedStartMap(FadeOut, group) - for group in groups - ]) - - -class FourierSeriesIllustraiton(Scene): - CONFIG = { - "n_range": range(1, 31, 2), - "axes_config": { - "number_line_config": { - "include_tip": False, - }, - "x_axis_config": { - "tick_frequency": 1 / 4, - "unit_size": 4, - }, - "x_min": 0, - "x_max": 1, - "y_min": -1, - "y_max": 1, - } - } - - def construct(self): - n_range = self.n_range - - axes1 = Axes(**self.axes_config) - axes1.x_axis.add_numbers( - 0.5, 1, - number_config={"num_decimal_places": 1} - ) - axes2 = axes1.copy() - target_func_graph = self.get_target_func_graph(axes2) - axes2.add(target_func_graph) - - arrow = Arrow(LEFT, RIGHT, color=WHITE) - group = VGroup(axes1, arrow, axes2) - group.arrange(RIGHT, buff=LARGE_BUFF) - group.shift(2 * UP) - group.shift_onto_screen() - - sine_graphs = VGroup(*[ - axes1.get_graph(self.generate_nth_func(n)) - for n in n_range - ]) - sine_graphs.set_stroke(width=3) - sine_graphs.set_color_by_gradient( - BLUE, GREEN, RED, YELLOW, PINK, - BLUE, GREEN, RED, YELLOW, PINK, - ) - - partial_sums = VGroup(*[ - axes1.get_graph(self.generate_kth_partial_sum_func(k + 1)) - for k in range(len(n_range)) - ]) - partial_sums.match_style(sine_graphs) - - sum_tex = self.get_sum_tex() - sum_tex.next_to(axes1, DOWN, LARGE_BUFF) - sum_tex.shift(RIGHT) - eq = TexMobject("=") - target_func_tex = self.get_target_func_tex() - target_func_tex.next_to(axes2, DOWN) - target_func_tex.match_y(sum_tex) - eq.move_to(midpoint( - target_func_tex.get_left(), - sum_tex.get_right() - )) - - range_words = TextMobject( - "For $0 \\le x \\le 1$" - ) - range_words.next_to( - VGroup(sum_tex, target_func_tex), - DOWN, - ) - - rects = it.chain( - [ - SurroundingRectangle(piece) - for piece in self.get_sum_tex_pieces(sum_tex) - ], - it.cycle([None]) - ) - - self.add(axes1, arrow, axes2) - self.add(target_func_graph) - self.add(sum_tex, eq, target_func_tex) - self.add(range_words) - - curr_partial_sum = axes1.get_graph(lambda x: 0) - curr_partial_sum.set_stroke(width=1) - for sine_graph, partial_sum, rect in zip(sine_graphs, partial_sums, rects): - anims1 = [ - ShowCreation(sine_graph) - ] - partial_sum.set_stroke(BLACK, 4, background=True) - anims2 = [ - curr_partial_sum.set_stroke, - {"width": 1, "opacity": 0.5}, - curr_partial_sum.set_stroke, - {"width": 0, "background": True}, - ReplacementTransform( - sine_graph, partial_sum, - remover=True - ), - ] - if rect: - rect.match_style(sine_graph) - anims1.append(ShowCreation(rect)) - anims2.append(FadeOut(rect)) - self.play(*anims1) - self.play(*anims2) - curr_partial_sum = partial_sum - - def get_sum_tex(self): - return TexMobject( - "\\frac{4}{\\pi} \\left(", - "\\frac{\\cos(\\pi x)}{1}", - "-\\frac{\\cos(3\\pi x)}{3}", - "+\\frac{\\cos(5\\pi x)}{5}", - "- \\cdots \\right)" - ).scale(0.75) - - def get_sum_tex_pieces(self, sum_tex): - return sum_tex[1:4] - - def get_target_func_tex(self): - step_tex = TexMobject( - """ - 1 \\quad \\text{if $x < 0.5$} \\\\ - 0 \\quad \\text{if $x = 0.5$} \\\\ - -1 \\quad \\text{if $x > 0.5$} \\\\ - """ - ) - lb = Brace(step_tex, LEFT, buff=SMALL_BUFF) - step_tex.add(lb) - return step_tex - - def get_target_func_graph(self, axes): - step_func = axes.get_graph( - lambda x: (1 if x < 0.5 else -1), - discontinuities=[0.5], - color=YELLOW, - stroke_width=3, - ) - dot = Dot(axes.c2p(0.5, 0), color=step_func.get_color()) - dot.scale(0.5) - step_func.add(dot) - return step_func - - # def generate_nth_func(self, n): - # return lambda x: (4 / n / PI) * np.sin(TAU * n * x) - - def generate_nth_func(self, n): - return lambda x: np.prod([ - (4 / PI), - (1 / n) * (-1)**((n - 1) / 2), - np.cos(PI * n * x) - ]) - - def generate_kth_partial_sum_func(self, k): - return lambda x: np.sum([ - self.generate_nth_func(n)(x) - for n in self.n_range[:k] - ]) - - -class FourierSeriesOfLineIllustration(FourierSeriesIllustraiton): - CONFIG = { - "n_range": range(1, 31, 2), - "axes_config": { - "y_axis_config": { - "unit_size": 2, - "tick_frequency": 0.25, - "numbers_with_elongated_ticks": [-1, 1], - } - } - } - - def get_sum_tex(self): - return TexMobject( - "\\frac{8}{\\pi^2} \\left(", - "\\frac{\\cos(\\pi x)}{1^2}", - "+\\frac{\\cos(3\\pi x)}{3^2}", - "+\\frac{\\cos(5\\pi x)}{5^2}", - "+ \\cdots \\right)" - ).scale(0.75) - - # def get_sum_tex_pieces(self, sum_tex): - # return sum_tex[1:4] - - def get_target_func_tex(self): - result = TexMobject("1 - 2x") - result.scale(1.5) - point = VectorizedPoint() - point.next_to(result, RIGHT, 1.5 * LARGE_BUFF) - # result.add(point) - return result - - def get_target_func_graph(self, axes): - return axes.get_graph( - lambda x: 1 - 2 * x, - color=YELLOW, - stroke_width=3, - ) - - # def generate_nth_func(self, n): - # return lambda x: (4 / n / PI) * np.sin(TAU * n * x) - - def generate_nth_func(self, n): - return lambda x: np.prod([ - (8 / PI**2), - (1 / n**2), - np.cos(PI * n * x) - ]) - - -class CircleAnimationOfF(FourierOfTrebleClef): - CONFIG = { - "height": 3, - "n_circles": 200, - "run_time": 10, - "arrow_config": { - "tip_length": 0.1, - "stroke_width": 2, - } - } - - def get_shape(self): - path = VMobject() - shape = TextMobject("F") - for sp in shape.family_members_with_points(): - path.append_points(sp.points) - return path - - -class ExponentialDecay(PiCreatureScene): - def construct(self): - k = 0.2 - mk_tex = "-0.2" - mk_tex_color = GREEN - t2c = {mk_tex: mk_tex_color} - - # Pi creature - randy = self.pi_creature - randy.flip() - randy.set_height(2.5) - randy.move_to(3 * RIGHT) - randy.to_edge(DOWN) - bubble = ThoughtBubble( - direction=LEFT, - height=3.5, - width=3, - ) - bubble.pin_to(randy) - bubble.set_fill(DARKER_GREY) - exp = TexMobject( - "Ce^{", mk_tex, "t}", - tex_to_color_map=t2c, - ) - exp.move_to(bubble.get_bubble_center()) - - # Setup axes - axes = Axes( - x_min=0, - x_max=13, - y_min=-4, - y_max=4, - ) - axes.set_stroke(width=2) - axes.set_color(LIGHT_GREY) - axes.scale(0.9) - axes.to_edge(LEFT, buff=LARGE_BUFF) - axes.x_axis.add_numbers() - axes.y_axis.add_numbers() - axes.y_axis.add_numbers(0) - axes.x_axis.add( - TextMobject("Time").next_to( - axes.x_axis.get_end(), DR, - ) - ) - axes.y_axis.add( - TexMobject("f").next_to( - axes.y_axis.get_corner(UR), RIGHT, - ).set_color(YELLOW) - ) - axes.x_axis.set_opacity(0) - - # Value trackers - y_tracker = ValueTracker(3) - x_tracker = ValueTracker(0) - dydt_tracker = ValueTracker() - dxdt_tracker = ValueTracker(0) - self.add( - y_tracker, x_tracker, - dydt_tracker, dxdt_tracker, - ) - - get_y = y_tracker.get_value - get_x = x_tracker.get_value - get_dydt = dydt_tracker.get_value - get_dxdt = dxdt_tracker.get_value - - dydt_tracker.add_updater(lambda m: m.set_value( - - k * get_y() - )) - y_tracker.add_updater(lambda m, dt: m.increment_value( - dt * get_dydt() - )) - x_tracker.add_updater(lambda m, dt: m.increment_value( - dt * get_dxdt() - )) - - # Tip/decimal - tip = ArrowTip(color=YELLOW) - tip.set_width(0.25) - tip.add_updater(lambda m: m.move_to( - axes.c2p(get_x(), get_y()), LEFT - )) - decimal = DecimalNumber() - decimal.add_updater(lambda d: d.set_value(get_y())) - decimal.add_updater(lambda d: d.next_to( - tip, RIGHT, - SMALL_BUFF, - )) - - # Rate of change arrow - arrow = Vector( - DOWN, color=RED, - max_stroke_width_to_length_ratio=50, - max_tip_length_to_length_ratio=0.2, - ) - arrow.set_stroke(width=4) - arrow.add_updater(lambda m: m.scale( - 2.5 * abs(get_dydt()) / m.get_length() - )) - arrow.add_updater(lambda m: m.move_to( - tip.get_left(), UP - )) - - # Graph - graph = TracedPath(tip.get_left) - - # Equation - ode = TexMobject( - "{d{f} \\over dt}(t)", - "=", mk_tex, "\\cdot {f}(t)", - tex_to_color_map={ - "{f}": YELLOW, - "=": WHITE, - mk_tex: mk_tex_color - } - ) - ode.to_edge(UP) - dfdt = ode[:3] - ft = ode[-2:] - - self.add(axes) - self.add(tip) - self.add(decimal) - self.add(arrow) - self.add(randy) - self.add(ode) - - # Show rate of change dependent on itself - rect = SurroundingRectangle(dfdt) - self.play(ShowCreation(rect)) - self.wait() - self.play( - Transform( - rect, - SurroundingRectangle(ft) - ) - ) - self.wait(3) - - # Show graph over time - self.play( - DrawBorderThenFill(bubble), - Write(exp), - FadeOut(rect), - randy.change, "thinking", - ) - axes.x_axis.set_opacity(1) - self.play( - y_tracker.set_value, 3, - ShowCreation(axes.x_axis), - ) - dxdt_tracker.set_value(1) - self.add(graph) - randy.add_updater(lambda r: r.look_at(tip)) - self.wait(4) - - # Show derivative of exponential - eq = TexMobject("=") - eq.next_to(ode.get_part_by_tex("="), DOWN, LARGE_BUFF) - exp.generate_target() - exp.target.next_to(eq, LEFT) - d_dt = TexMobject("{d \\over dt}") - d_dt.next_to(exp.target, LEFT) - const = TexMobject(mk_tex) - const.set_color(mk_tex_color) - dot = TexMobject("\\cdot") - const.next_to(eq, RIGHT) - dot.next_to(const, RIGHT, 2 * SMALL_BUFF) - exp_copy = exp.copy() - exp_copy.next_to(dot, RIGHT, 2 * SMALL_BUFF) - VGroup(const, dot, eq).align_to(exp_copy, DOWN) - - self.play( - MoveToTarget(exp), - FadeOut(bubble), - FadeIn(d_dt), - FadeIn(eq), - ) - self.wait(2) - self.play( - ApplyMethod( - exp[1].copy().replace, - const[0], - ) - ) - self.wait() - rect = SurroundingRectangle(exp) - rect.set_stroke(BLUE, 2) - self.play(FadeIn(rect)) - self.play( - Write(dot), - TransformFromCopy(exp, exp_copy), - rect.move_to, exp_copy - ) - self.play(FadeOut(rect)) - self.wait(5) - - -class InvestmentGrowth(Scene): - CONFIG = { - "output_tex": "{M}", - "output_color": GREEN, - "initial_value": 1, - "initial_value_tex": "{M_0}", - "k": 0.05, - "k_tex": "0.05", - "total_time": 43, - "time_rate": 3, - } - - def construct(self): - # Axes - axes = Axes( - x_min=0, - x_max=self.total_time, - y_min=0, - y_max=6, - x_axis_config={ - "unit_size": 0.3, - "tick_size": 0.05, - "numbers_with_elongated_ticks": range( - 0, self.total_time, 5 - ) - } - ) - axes.to_corner(DL, buff=LARGE_BUFF) - - time_label = TextMobject("Time") - time_label.next_to( - axes.x_axis.get_right(), - UP, MED_LARGE_BUFF - ) - time_label.shift_onto_screen() - axes.x_axis.add(time_label) - money_label = TexMobject(self.output_tex) - money_label.set_color(self.output_color) - money_label.next_to( - axes.y_axis.get_top(), - UP, - ) - axes.y_axis.add(money_label) - - # Graph - graph = axes.get_graph( - lambda x: self.initial_value * np.exp(self.k * x) - ) - graph.set_color(self.output_color) - full_graph = graph.copy() - time_tracker = self.get_time_tracker() - graph.add_updater(lambda m: m.pointwise_become_partial( - full_graph, 0, - np.clip( - time_tracker.get_value() / self.total_time, - 0, 1, - ) - )) - - # Equation - tex_kwargs = { - "tex_to_color_map": { - self.output_tex: self.output_color, - self.initial_value_tex: BLUE, - } - } - ode = TexMobject( - "{d", - "\\over dt}", - self.output_tex, - "(t)", - "=", self.k_tex, - "\\cdot", self.output_tex, "(t)", - **tex_kwargs - ) - ode.to_edge(UP) - exp = TexMobject( - self.output_tex, - "(t) =", self.initial_value_tex, - "e^{", self.k_tex, "t}", - **tex_kwargs - ) - exp.next_to(ode, DOWN, LARGE_BUFF) - - M0_part = exp.get_part_by_tex(self.initial_value_tex) - exp_part = exp[-3:] - M0_label = M0_part.copy() - M0_label.next_to( - axes.c2p(0, self.initial_value), - LEFT - ) - M0_part.set_opacity(0) - exp_part.save_state() - exp_part.align_to(M0_part, LEFT) - - self.add(axes) - self.add(graph) - self.add(time_tracker) - - self.play(FadeInFromDown(ode)) - self.wait(6) - self.play(FadeInFrom(exp, UP)) - self.wait(2) - self.play( - Restore(exp_part), - M0_part.set_opacity, 1, - ) - self.play(TransformFromCopy( - M0_part, M0_label - )) - self.wait(5) - - def get_time_tracker(self): - time_tracker = ValueTracker(0) - time_tracker.add_updater( - lambda m, dt: m.increment_value( - self.time_rate * dt - ) - ) - return time_tracker - - -class GrowingPileOfMoney(InvestmentGrowth): - CONFIG = { - "total_time": 60 - } - - def construct(self): - initial_count = 5 - k = self.k - total_time = self.total_time - - time_tracker = self.get_time_tracker() - - final_count = initial_count * np.exp(k * total_time) - dollar_signs = VGroup(*[ - TexMobject("\\$") - for x in range(int(final_count)) - ]) - dollar_signs.set_color(GREEN) - for ds in dollar_signs: - ds.shift( - 3 * np.random.random(3) - ) - dollar_signs.center() - dollar_signs.sort(get_norm) - dollar_signs.set_stroke(BLACK, 3, background=True) - - def update_dollar_signs(group): - t = time_tracker.get_value() - count = initial_count * np.exp(k * t) - alpha = count / final_count - n, sa = integer_interpolate(0, len(dollar_signs), alpha) - group.set_opacity(1) - group[n:].set_opacity(0) - group[n].set_opacity(sa) - - dollar_signs.add_updater(update_dollar_signs) - - self.add(time_tracker) - self.add(dollar_signs) - self.wait(20) - - -class CarbonDecayCurve(InvestmentGrowth): - CONFIG = { - "output_tex": "{^{14}C}", - "output_color": GOLD, - "initial_value": 4, - "initial_value_tex": "{^{14}C_0}", - "k": -0.1, - "k_tex": "-k", - "time_rate": 6, - } - - -class CarbonDecayingInMammoth(Scene): - def construct(self): - mammoth = SVGMobject("Mammoth") - mammoth.set_color( - interpolate_color(GREY_BROWN, WHITE, 0.25) - ) - mammoth.set_height(4) - body = mammoth[9] - - atoms = VGroup(*[ - self.get_atom(body) - for n in range(600) - ]) - - p_decay = 0.2 - - def update_atoms(group, dt): - for atom in group: - if np.random.random() < dt * p_decay: - group.remove(atom) - return group - atoms.add_updater(update_atoms) - - self.add(mammoth) - self.add(atoms) - self.wait(20) - - def get_atom(self, body): - atom = Dot(color=GOLD) - atom.set_height(0.05) - - dl = body.get_corner(DL) - ur = body.get_corner(UR) - - wn = 0 - while wn == 0: - point = np.array([ - interpolate(dl[0], ur[0], np.random.random()), - interpolate(dl[1], ur[1], np.random.random()), - 0 - ]) - wn = get_winding_number([ - body.point_from_proportion(a) - point - for a in np.linspace(0, 1, 300) - ]) - wn = int(np.round(wn)) - - atom.move_to(point) - return atom - - -class BoundaryConditionInterlude(Scene): - def construct(self): - background = FullScreenFadeRectangle( - fill_color=DARK_GREY - ) - storyline = self.get_main_storyline() - storyline.generate_target() - v_shift = 2 * DOWN - storyline.target.shift(v_shift) - im_to_im = storyline[1].get_center() - storyline[0].get_center() - - bc_image = self.get_labeled_image( - "Boundary\\\\conditions", - "boundary_condition_thumbnail" - ) - bc_image.next_to(storyline[1], UP) - new_arrow0 = Arrow( - storyline.arrows[0].get_start() + v_shift, - bc_image.get_left() + SMALL_BUFF * LEFT, - path_arc=-90 * DEGREES, - buff=0, - ) - new_mid_arrow = Arrow( - bc_image.get_bottom(), - storyline[1].get_top() + v_shift, - buff=SMALL_BUFF, - path_arc=60 * DEGREES, - ) - - # BC detour - self.add(background) - self.add(storyline[0]) - for im1, im2, arrow in zip(storyline, storyline[1:], storyline.arrows): - self.add(im2, im1) - self.play( - FadeInFrom(im2, -im_to_im), - ShowCreation(arrow), - ) - self.wait() - self.play( - GrowFromCenter(bc_image), - MoveToTarget(storyline), - Transform( - storyline.arrows[0], - new_arrow0, - ), - MaintainPositionRelativeTo( - storyline.arrows[1], - storyline, - ), - ) - self.play(ShowCreation(new_mid_arrow)) - self.wait() - - # From BC to next step - rect1 = bc_image[2].copy() - rect2 = storyline[1][2].copy() - rect3 = storyline[2][2].copy() - for rect in rect1, rect2, rect3: - rect.set_stroke(YELLOW, 3) - - self.play(FadeIn(rect1)) - kw = {"path_arc": 60 * DEGREES} - self.play( - LaggedStart( - Transform(rect1, rect2, **kw), - # TransformFromCopy(rect1, rect3, **kw), - lag_ratio=0.4, - ) - ) - self.play( - FadeOut(rect1), - # FadeOut(rect3), - ) - - # Reorganize images - im1, im3, im4 = storyline - im2 = bc_image - l_group = Group(im1, im2) - r_group = Group(im3, im4) - for group in l_group, r_group: - group.generate_target() - group.target.arrange(DOWN, buff=LARGE_BUFF) - group.target.center() - - l_group.target.to_edge(LEFT) - r_group.target.move_to( - FRAME_WIDTH * RIGHT / 4 - ) - brace = Brace(r_group.target, LEFT) - nv_text = brace.get_text("Next\\\\video") - nv_text.scale(1.5, about_edge=RIGHT) - nv_text.set_color(YELLOW) - brace.set_color(YELLOW) - - arrows = VGroup( - storyline.arrows, - new_mid_arrow, - ) - - self.play( - LaggedStart( - MoveToTarget(l_group), - MoveToTarget(r_group), - lag_ratio=0.3, - ), - FadeOut(arrows), - ) - self.play( - GrowFromCenter(brace), - FadeInFrom(nv_text, RIGHT) - ) - self.wait() - - def get_main_storyline(self): - images = Group( - self.get_sine_curve_image(), - self.get_linearity_image(), - self.get_fourier_series_image(), - ) - for image in images: - image.set_height(3) - images.arrange(RIGHT, buff=1) - images.set_width(FRAME_WIDTH - 1) - - arrows = VGroup() - for im1, im2 in zip(images, images[1:]): - arrow = Arrow( - im1.get_top(), - im2.get_top(), - color=WHITE, - buff=MED_SMALL_BUFF, - path_arc=-120 * DEGREES, - rectangular_stem_width=0.025, - ) - arrow.scale(0.7, about_edge=DOWN) - arrows.add(arrow) - images.arrows = arrows - - return images - - def get_sine_curve_image(self): - return self.get_labeled_image( - "Sine curves", - "sine_curve_temp_graph", - ) - - def get_linearity_image(self): - return self.get_labeled_image( - "Linearity", - "linearity_thumbnail", - ) - - def get_fourier_series_image(self): - return self.get_labeled_image( - "Fourier series", - "fourier_series_thumbnail", - ) - - def get_labeled_image(self, text, image_file): - rect = ScreenRectangle(height=2) - border = rect.copy() - rect.set_fill(BLACK, 1) - rect.set_stroke(WHITE, 0) - border.set_stroke(WHITE, 2) - - text_mob = TextMobject(text) - text_mob.set_stroke(BLACK, 5, background=True) - text_mob.next_to(rect.get_top(), DOWN, SMALL_BUFF) - - image = ImageMobject(image_file) - image.replace(rect, dim_to_match=1) - image.scale(0.8, about_edge=DOWN) - - return Group(rect, image, border, text_mob) - - -class GiantCross(Scene): - def construct(self): - rect = FullScreenFadeRectangle() - cross = Cross(rect) - cross.set_stroke(RED, 25) - - words = TextMobject("This wouldn't\\\\happen!") - words.scale(2) - words.set_color(RED) - words.to_edge(UP) - - self.play( - FadeInFromDown(words), - ShowCreation(cross), - ) - self.wait() - - -class EndScreen(PatreonEndScreen): - CONFIG = { - "specific_patrons": [ - "1stViewMaths", - "Adrian Robinson", - "Alexis Olson", - "Andreas Benjamin Brössel", - "Andrew Busey", - "Ankalagon", - "Antoine Bruguier", - "Antonio Juarez", - "Arjun Chakroborty", - "Art Ianuzzi", - "Awoo", - "Ayan Doss", - "AZsorcerer", - "Barry Fam", - "Bernd Sing", - "Boris Veselinovich", - "Brian Staroselsky", - "Charles Southerland", - "Chris Connett", - "Christian Kaiser", - "Clark Gaebel", - "Cooper Jones", - "Danger Dai", - "Daniel Pang", - "Dave B", - "Dave Kester", - "David B. Hill", - "David Clark", - "Delton Ding", - "eaglle", - "Empirasign", - "emptymachine", - "Eric Younge", - "Eryq Ouithaqueue", - "Federico Lebron", - "Fernando Via Canel", - "Giovanni Filippi", - "Hal Hildebrand", - "Hitoshi Yamauchi", - "Isaac Jeffrey Lee", - "j eduardo perez", - "Jacob Hartmann", - "Jacob Magnuson", - "Jameel Syed", - "Jason Hise", - "Jeff Linse", - "Jeff Straathof", - "John C. Vesey", - "John Griffith", - "John Haley", - "John V Wertheim", - "Jonathan Eppele", - "Kai-Siang Ang", - "Kanan Gill", - "Kartik\\\\Cating-Subramanian", - "L0j1k", - "Lee Redden", - "Linh Tran", - "Ludwig Schubert", - "Magister Mugit", - "Mark B Bahu", - "Martin Price", - "Mathias Jansson", - "Matt Langford", - "Matt Roveto", - "Matthew Bouchard", - "Matthew Cocke", - "Michael Faust", - "Michael Hardel", - "Mirik Gogri", - "Mustafa Mahdi", - "Márton Vaitkus", - "Nero Li", - "Nikita Lesnikov", - "Omar Zrien", - "Owen Campbell-Moore", - "Patrick", - "Peter Ehrnstrom", - "RedAgent14", - "rehmi post", - "Richard Comish", - "Ripta Pasay", - "Rish Kundalia", - "Robert Teed", - "Roobie", - "Ryan Williams", - "Sebastian Garcia", - "Solara570", - "Stephan Arlinghaus", - "Steven Siddals", - "Stevie Metke", - "Tal Einav", - "Ted Suzman", - "Thomas Tarler", - "Tianyu Ge", - "Tom Fleming", - "Valeriy Skobelev", - "Xuanji Li", - "Yavor Ivanov", - "YinYangBalance.Asia", - "Zach Cardwell", - "Luc Ritchie", - "Britt Selvitelle", - "David Gow", - "J", - "Jonathan Wilson", - "Joseph John Cox", - "Magnus Dahlström", - "Randy C. Will", - "Ryan Atallah", - "Lukas -krtek.net- Novy", - "Jordan Scales", - "Ali Yahya", - "Arthur Zey", - "Atul S", - "dave nicponski", - "Evan Phillips", - "Joseph Kelly", - "Kaustuv DeBiswas", - "Lambda AI Hardware", - "Lukas Biewald", - "Mark Heising", - "Mike Coleman", - "Nicholas Cahill", - "Peter Mcinerney", - "Quantopian", - "Roy Larson", - "Scott Walter, Ph.D.", - "Yana Chernobilsky", - "Yu Jun", - "D. Sivakumar", - "Richard Barthel", - "Burt Humburg", - "Matt Russell", - "Scott Gray", - "soekul", - "Tihan Seale", - "Juan Benet", - "Vassili Philippov", - "Kurt Dicus", - ], - } diff --git a/active_projects/ode/part3/temperature_graphs.py b/active_projects/ode/part3/temperature_graphs.py deleted file mode 100644 index 2ed445b5..00000000 --- a/active_projects/ode/part3/temperature_graphs.py +++ /dev/null @@ -1,2830 +0,0 @@ -from scipy import integrate - -from manimlib.imports import * -from active_projects.ode.part2.heat_equation import * - - -class TemperatureGraphScene(SpecialThreeDScene): - CONFIG = { - "axes_config": { - "x_min": 0, - "x_max": TAU, - "y_min": 0, - "y_max": 10, - "z_min": -3, - "z_max": 3, - "x_axis_config": { - "tick_frequency": TAU / 8, - "include_tip": False, - }, - "num_axis_pieces": 1, - }, - "default_graph_style": { - "stroke_width": 2, - "stroke_color": WHITE, - "background_image_file": "VerticalTempGradient", - }, - "default_surface_config": { - "fill_opacity": 0.1, - "checkerboard_colors": [LIGHT_GREY], - "stroke_width": 0.5, - "stroke_color": WHITE, - "stroke_opacity": 0.5, - }, - "temp_text": "Temperature", - } - - def get_three_d_axes(self, include_labels=True, include_numbers=False, **kwargs): - config = dict(self.axes_config) - config.update(kwargs) - axes = ThreeDAxes(**config) - axes.set_stroke(width=2) - - if include_numbers: - self.add_axes_numbers(axes) - - if include_labels: - self.add_axes_labels(axes) - - # Adjust axis orientation - axes.x_axis.rotate( - 90 * DEGREES, RIGHT, - about_point=axes.c2p(0, 0, 0), - ) - axes.y_axis.rotate( - 90 * DEGREES, UP, - about_point=axes.c2p(0, 0, 0), - ) - - # Add xy-plane - input_plane = self.get_surface( - axes, lambda x, t: 0 - ) - input_plane.set_style( - fill_opacity=0.5, - fill_color=BLUE_B, - stroke_width=0.5, - stroke_color=WHITE, - ) - - axes.input_plane = input_plane - - return axes - - def add_axes_numbers(self, axes): - x_axis = axes.x_axis - y_axis = axes.y_axis - tex_vals = [ - ("\\pi \\over 2", TAU / 4), - ("\\pi", TAU / 2), - ("3\\pi \\over 2", 3 * TAU / 4), - ("2\\pi", TAU) - ] - x_labels = VGroup() - for tex, val in tex_vals: - label = TexMobject(tex) - label.scale(0.5) - label.next_to(x_axis.n2p(val), DOWN) - x_labels.add(label) - x_axis.add(x_labels) - x_axis.numbers = x_labels - - y_axis.add_numbers() - for number in y_axis.numbers: - number.rotate(90 * DEGREES) - return axes - - def add_axes_labels(self, axes): - x_label = TexMobject("x") - x_label.next_to(axes.x_axis.get_end(), RIGHT) - axes.x_axis.label = x_label - - t_label = TextMobject("Time") - t_label.rotate(90 * DEGREES, OUT) - t_label.next_to(axes.y_axis.get_end(), UP) - axes.y_axis.label = t_label - - temp_label = TextMobject(self.temp_text) - temp_label.rotate(90 * DEGREES, RIGHT) - temp_label.next_to(axes.z_axis.get_zenith(), RIGHT) - axes.z_axis.label = temp_label - for axis in axes: - axis.add(axis.label) - return axes - - def get_time_slice_graph(self, axes, func, t, **kwargs): - config = dict() - config.update(self.default_graph_style) - config.update({ - "t_min": axes.x_min, - "t_max": axes.x_max, - }) - config.update(kwargs) - return ParametricFunction( - lambda x: axes.c2p( - x, t, func(x, t) - ), - **config, - ) - - def get_initial_state_graph(self, axes, func, **kwargs): - return self.get_time_slice_graph( - axes, - lambda x, t: func(x), - t=0, - **kwargs - ) - - def get_surface(self, axes, func, **kwargs): - config = { - "u_min": axes.x_min, - "u_max": axes.x_max, - "v_min": axes.y_min, - "v_max": axes.y_max, - "resolution": ( - (axes.x_max - axes.x_min) // axes.x_axis.tick_frequency, - (axes.y_max - axes.y_min) // axes.y_axis.tick_frequency, - ), - } - config.update(self.default_surface_config) - config.update(kwargs) - return ParametricSurface( - lambda x, t: axes.c2p( - x, t, func(x, t) - ), - **config - ) - - def orient_three_d_mobject(self, mobject, - phi=85 * DEGREES, - theta=-80 * DEGREES): - mobject.rotate(-90 * DEGREES - theta, OUT) - mobject.rotate(phi, LEFT) - return mobject - - def get_rod_length(self): - return self.axes_config["x_max"] - - def get_const_time_plane(self, axes): - t_tracker = ValueTracker(0) - plane = Polygon( - *[ - axes.c2p(x, 0, z) - for x, z in [ - (axes.x_min, axes.z_min), - (axes.x_max, axes.z_min), - (axes.x_max, axes.z_max), - (axes.x_min, axes.z_max), - ] - ], - stroke_width=0, - fill_color=WHITE, - fill_opacity=0.2 - ) - plane.add_updater(lambda m: m.move_to( - axes.c2p( - axes.x_min, - t_tracker.get_value(), - axes.z_min, - ), - IN + LEFT, - )) - plane.t_tracker = t_tracker - return plane - - -class SimpleCosExpGraph(TemperatureGraphScene): - def construct(self): - axes = self.get_three_d_axes() - cos_graph = self.get_cos_graph(axes) - cos_exp_surface = self.get_cos_exp_surface(axes) - - self.set_camera_orientation( - phi=80 * DEGREES, - theta=-80 * DEGREES, - ) - self.camera.frame_center.shift(3 * RIGHT) - self.begin_ambient_camera_rotation(rate=0.01) - - self.add(axes) - self.play(ShowCreation(cos_graph)) - self.play(UpdateFromAlphaFunc( - cos_exp_surface, - lambda m, a: m.become( - self.get_cos_exp_surface(axes, v_max=a * 10) - ), - run_time=3 - )) - - self.add(cos_graph.copy()) - - t_tracker = ValueTracker(0) - get_t = t_tracker.get_value - cos_graph.add_updater( - lambda m: m.become(self.get_time_slice_graph( - axes, - lambda x: self.cos_exp(x, get_t()), - t=get_t() - )) - ) - - plane = Rectangle( - stroke_width=0, - fill_color=WHITE, - fill_opacity=0.1, - ) - plane.rotate(90 * DEGREES, RIGHT) - plane.match_width(axes.x_axis) - plane.match_depth(axes.z_axis, stretch=True) - plane.move_to(axes.c2p(0, 0, 0), LEFT) - - self.add(plane, cos_graph) - self.play( - ApplyMethod( - t_tracker.set_value, 10, - run_time=10, - rate_func=linear, - ), - ApplyMethod( - plane.shift, 10 * UP, - run_time=10, - rate_func=linear, - ), - VFadeIn(plane), - ) - self.wait(10) - - # - def cos_exp(self, x, t, A=2, omega=1.5, k=0.1): - return A * np.cos(omega * x) * np.exp(-k * (omega**2) * t) - - def get_cos_graph(self, axes, **config): - return self.get_initial_state_graph( - axes, - lambda x: self.cos_exp(x, 0), - **config - ) - - def get_cos_exp_surface(self, axes, **config): - return self.get_surface( - axes, - lambda x, t: self.cos_exp(x, t), - **config - ) - - -class AddMultipleSolutions(SimpleCosExpGraph): - CONFIG = { - "axes_config": { - "x_axis_config": { - "unit_size": 0.7, - }, - } - } - - def construct(self): - axes1, axes2, axes3 = all_axes = VGroup(*[ - self.get_three_d_axes( - include_labels=False, - ) - for x in range(3) - ]) - all_axes.scale(0.5) - self.orient_three_d_mobject(all_axes) - - As = [1.5, 1.5] - omegas = [1.5, 2.5] - ks = [0.1, 0.1] - quads = [ - (axes1, [As[0]], [omegas[0]], [ks[0]]), - (axes2, [As[1]], [omegas[1]], [ks[1]]), - (axes3, As, omegas, ks), - ] - - for axes, As, omegas, ks in quads: - graph = self.get_initial_state_graph( - axes, - lambda x: np.sum([ - self.cos_exp(x, 0, A, omega, k) - for A, omega, k in zip(As, omegas, ks) - ]) - ) - surface = self.get_surface( - axes, - lambda x, t: np.sum([ - self.cos_exp(x, t, A, omega, k) - for A, omega, k in zip(As, omegas, ks) - ]) - ) - surface.sort(lambda p: -p[2]) - - axes.add(surface, graph) - axes.graph = graph - axes.surface = surface - - self.set_camera_orientation(distance=100) - plus = TexMobject("+").scale(2) - equals = TexMobject("=").scale(2) - group = VGroup( - axes1, plus, axes2, equals, axes3, - ) - group.arrange(RIGHT, buff=SMALL_BUFF) - - for axes in all_axes: - checkmark = TexMobject("\\checkmark") - checkmark.set_color(GREEN) - checkmark.scale(2) - checkmark.next_to(axes, UP) - checkmark.shift(0.7 * DOWN) - axes.checkmark = checkmark - - self.add(axes1, axes2) - self.play( - LaggedStart( - Write(axes1.surface), - Write(axes2.surface), - ), - LaggedStart( - FadeInFrom(axes1.checkmark, DOWN), - FadeInFrom(axes2.checkmark, DOWN), - ), - lag_ratio=0.2, - run_time=1, - ) - self.wait() - self.play(Write(plus)) - self.play( - Transform( - axes1.copy().set_fill(opacity=0), - axes3 - ), - Transform( - axes2.copy().set_fill(opacity=0), - axes3 - ), - FadeInFrom(equals, LEFT) - ) - self.play( - FadeInFrom(axes3.checkmark, DOWN), - ) - self.wait() - - -class BreakDownAFunction(SimpleCosExpGraph): - CONFIG = { - "axes_config": { - "z_axis_config": { - "unit_size": 0.75, - "include_tip": False, - }, - "z_min": -2, - "y_max": 20, - }, - "n_low_axes": 4, - "k": 0.2, - } - - def construct(self): - self.set_camera_orientation(distance=100) - self.set_axes() - self.setup_graphs() - self.show_break_down() - self.show_solutions_for_waves() - - def set_axes(self): - top_axes = self.get_three_d_axes() - top_axes.z_axis.label.next_to( - top_axes.z_axis.get_end(), OUT, SMALL_BUFF - ) - top_axes.y_axis.set_opacity(0) - self.orient_three_d_mobject(top_axes) - top_axes.y_axis.label.rotate(-10 * DEGREES, UP) - top_axes.scale(0.75) - top_axes.center() - top_axes.to_edge(UP) - - low_axes = self.get_three_d_axes( - z_min=-3, - z_axis_config={"unit_size": 1} - ) - low_axes.y_axis.set_opacity(0) - for axis in low_axes: - axis.label.fade(1) - # low_axes.add(low_axes.input_plane) - # low_axes.input_plane.set_opacity(0) - - self.orient_three_d_mobject(low_axes) - low_axes_group = VGroup(*[ - low_axes.deepcopy() - for x in range(self.n_low_axes) - ]) - low_axes_group.arrange( - RIGHT, buff=low_axes.get_width() / 3 - ) - low_axes_group.set_width(FRAME_WIDTH - 2.5) - low_axes_group.next_to(top_axes, DOWN, LARGE_BUFF) - low_axes_group.to_edge(LEFT) - - self.top_axes = top_axes - self.low_axes_group = low_axes_group - - def setup_graphs(self): - top_axes = self.top_axes - low_axes_group = self.low_axes_group - - top_graph = self.get_initial_state_graph( - top_axes, - self.initial_func, - discontinuities=self.get_initial_func_discontinuities(), - color=YELLOW, - ) - top_graph.set_stroke(width=4) - - fourier_terms = self.get_fourier_cosine_terms( - self.initial_func - ) - - low_graphs = VGroup(*[ - self.get_initial_state_graph( - axes, - lambda x: A * np.cos(n * x / 2) - ) - for n, axes, A in zip( - it.count(), - low_axes_group, - fourier_terms - ) - ]) - k = self.k - low_surfaces = VGroup(*[ - self.get_surface( - axes, - lambda x, t: np.prod([ - A, - np.cos(n * x / 2), - np.exp(-k * (n / 2)**2 * t) - ]) - ) - for n, axes, A in zip( - it.count(), - low_axes_group, - fourier_terms - ) - ]) - top_surface = self.get_surface( - top_axes, - lambda x, t: np.sum([ - np.prod([ - A, - np.cos(n * x / 2), - np.exp(-k * (n / 2)**2 * t) - ]) - for n, A in zip( - it.count(), - fourier_terms - ) - ]) - ) - - self.top_graph = top_graph - self.low_graphs = low_graphs - self.low_surfaces = low_surfaces - self.top_surface = top_surface - - def show_break_down(self): - top_axes = self.top_axes - low_axes_group = self.low_axes_group - top_graph = self.top_graph - low_graphs = self.low_graphs - - plusses = VGroup(*[ - TexMobject("+").next_to( - axes.x_axis.get_end(), - RIGHT, MED_SMALL_BUFF - ) - for axes in low_axes_group - ]) - dots = TexMobject("\\cdots") - dots.next_to(plusses, RIGHT, MED_SMALL_BUFF) - - arrow = Arrow( - dots.get_right(), - top_graph.get_end() + 1.4 * DOWN + 1.7 * RIGHT, - path_arc=90 * DEGREES, - ) - - top_words = TextMobject("Arbitrary\\\\function") - top_words.next_to(top_axes, LEFT, MED_LARGE_BUFF) - top_words.set_color(YELLOW) - top_arrow = Arrow( - top_words.get_right(), - top_graph.point_from_proportion(0.3) - ) - - low_words = TextMobject("Sine curves") - low_words.set_color(BLUE) - low_words.next_to(low_axes_group, DOWN, MED_LARGE_BUFF) - - self.add(top_axes) - self.play(ShowCreation(top_graph)) - self.play( - FadeInFrom(top_words, RIGHT), - ShowCreation(top_arrow) - ) - self.wait() - self.play( - LaggedStartMap(FadeIn, low_axes_group), - FadeInFrom(low_words, UP), - LaggedStartMap(FadeInFromDown, [*plusses, dots]), - *[ - TransformFromCopy(top_graph, low_graph) - for low_graph in low_graphs - ], - ) - self.play(ShowCreation(arrow)) - self.wait() - - def show_solutions_for_waves(self): - low_axes_group = self.low_axes_group - top_axes = self.top_axes - low_graphs = self.low_graphs - low_surfaces = self.low_surfaces - top_surface = self.top_surface - top_graph = self.top_graph - - for surface in [top_surface, *low_surfaces]: - surface.sort(lambda p: -p[2]) - - anims1 = [] - anims2 = [ - ApplyMethod( - top_axes.y_axis.set_opacity, 1, - ), - ] - for axes, surface, graph in zip(low_axes_group, low_surfaces, low_graphs): - axes.y_axis.set_opacity(1) - axes.y_axis.label.fade(1) - anims1 += [ - ShowCreation(axes.y_axis), - Write(surface, run_time=2), - ] - anims2.append(AnimationGroup( - TransformFromCopy(graph, top_graph.copy()), - Transform( - surface.copy().set_fill(opacity=0), - top_surface, - ) - )) - - self.play(*anims1) - self.wait() - self.play(LaggedStart(*anims2, run_time=2)) - self.wait() - - checkmark = TexMobject("\\checkmark") - checkmark.set_color(GREEN) - low_checkmarks = VGroup(*[ - checkmark.copy().next_to( - surface.get_top(), UP, SMALL_BUFF - ) - for surface in low_surfaces - ]) - top_checkmark = checkmark.copy() - top_checkmark.scale(1.5) - top_checkmark.move_to(top_axes.get_corner(UR)) - - self.play(LaggedStartMap(FadeInFromDown, low_checkmarks)) - self.wait() - self.play(*[ - TransformFromCopy(low_checkmark, top_checkmark.copy()) - for low_checkmark in low_checkmarks - ]) - self.wait() - - # - def initial_func(self, x): - # return 3 * np.exp(-(x - PI)**2) - - x1 = TAU / 4 - 1 - x2 = TAU / 4 + 1 - x3 = 3 * TAU / 4 - 1.6 - x4 = 3 * TAU / 4 + 0.3 - - T0 = -2 - T1 = 2 - T2 = 1 - - if x < x1: - return T0 - elif x < x2: - alpha = inverse_interpolate(x1, x2, x) - return bezier([T0, T0, T1, T1])(alpha) - elif x < x3: - return T1 - elif x < x4: - alpha = inverse_interpolate(x3, x4, x) - return bezier([T1, T1, T2, T2])(alpha) - else: - return T2 - - def get_initial_func_discontinuities(self): - # return [TAU / 4, 3 * TAU / 4] - return [] - - def get_fourier_cosine_terms(self, func, n_terms=40): - result = [ - integrate.quad( - lambda x: (1 / PI) * func(x) * np.cos(n * x / 2), - 0, TAU - )[0] - for n in range(n_terms) - ] - result[0] = result[0] / 2 - return result - - -class OceanOfPossibilities(TemperatureGraphScene): - CONFIG = { - "axes_config": { - "z_min": 0, - "z_max": 4, - }, - "k": 0.2, - "default_surface_config": { - # "resolution": (32, 20), - # "resolution": (8, 5), - } - } - - def construct(self): - self.setup_camera() - self.setup_axes() - self.setup_surface() - self.show_solution() - self.reference_boundary_conditions() - self.reference_initial_condition() - self.ambiently_change_solution() - - def setup_camera(self): - self.set_camera_orientation( - phi=80 * DEGREES, - theta=-80 * DEGREES, - ) - self.begin_ambient_camera_rotation(rate=0.01) - - def setup_axes(self): - axes = self.get_three_d_axes(include_numbers=True) - axes.add(axes.input_plane) - axes.scale(0.8) - axes.center() - axes.shift(OUT + RIGHT) - - self.add(axes) - self.axes = axes - - def setup_surface(self): - axes = self.axes - k = self.k - - # Parameters for surface function - initial_As = [2] + [ - 0.8 * random.choice([-1, 1]) / n - for n in range(1, 20) - ] - A_trackers = Group(*[ - ValueTracker(A) - for A in initial_As - ]) - - def get_As(): - return [At.get_value() for At in A_trackers] - - omegas = [n / 2 for n in range(0, 10)] - - def func(x, t): - return np.sum([ - np.prod([ - A * np.cos(omega * x), - np.exp(-k * omega**2 * t) - ]) - for A, omega in zip(get_As(), omegas) - ]) - - # Surface and graph - surface = always_redraw( - lambda: self.get_surface(axes, func) - ) - t_tracker = ValueTracker(0) - graph = always_redraw( - lambda: self.get_time_slice_graph( - axes, func, t_tracker.get_value(), - ) - ) - - surface.suspend_updating() - graph.suspend_updating() - - self.surface_func = func - self.surface = surface - self.graph = graph - self.t_tracker = t_tracker - self.A_trackers = A_trackers - self.omegas = omegas - - def show_solution(self): - axes = self.axes - surface = self.surface - graph = self.graph - t_tracker = self.t_tracker - get_t = t_tracker.get_value - - opacity_tracker = ValueTracker(0) - plane = always_redraw(lambda: Polygon( - *[ - axes.c2p(x, get_t(), T) - for x, T in [ - (0, 0), (TAU, 0), (TAU, 4), (0, 4) - ] - ], - stroke_width=0, - fill_color=WHITE, - fill_opacity=opacity_tracker.get_value(), - )) - - self.add(surface, plane, graph) - graph.resume_updating() - self.play( - opacity_tracker.set_value, 0.2, - ApplyMethod( - t_tracker.set_value, 1, - rate_func=linear - ), - run_time=1 - ) - self.play( - ApplyMethod( - t_tracker.set_value, 10, - rate_func=linear, - run_time=9 - ) - ) - self.wait() - - self.plane = plane - - def reference_boundary_conditions(self): - axes = self.axes - t_numbers = axes.y_axis.numbers - - lines = VGroup(*[ - Line( - axes.c2p(x, 0, 0), - axes.c2p(x, axes.y_max, 0), - stroke_width=3, - stroke_color=MAROON_B, - ) - for x in [0, axes.x_max] - ]) - surface_boundary_lines = always_redraw(lambda: VGroup(*[ - ParametricFunction( - lambda t: axes.c2p( - x, t, - self.surface_func(x, t) - ), - t_max=axes.y_max - ).match_style(self.graph) - for x in [0, axes.x_max] - ])) - # surface_boundary_lines.suspend_updating() - words = VGroup() - for line in lines: - word = TextMobject("Boundary") - word.set_stroke(BLACK, 3, background=True) - word.scale(1.5) - word.match_color(line) - word.rotate(90 * DEGREES, RIGHT) - word.rotate(90 * DEGREES, OUT) - word.next_to(line, OUT, SMALL_BUFF) - words.add(word) - - self.stop_ambient_camera_rotation() - self.move_camera( - theta=-45 * DEGREES, - added_anims=[ - LaggedStartMap(ShowCreation, lines), - LaggedStartMap( - FadeInFrom, words, - lambda m: (m, IN) - ), - FadeOut(t_numbers), - ] - ) - self.play( - LaggedStart(*[ - TransformFromCopy(l1, l2) - for l1, l2 in zip(lines, surface_boundary_lines) - ]) - ) - self.add(surface_boundary_lines) - self.wait() - self.move_camera( - theta=-70 * DEGREES, - ) - - self.surface_boundary_lines = surface_boundary_lines - - def reference_initial_condition(self): - plane = self.plane - t_tracker = self.t_tracker - - self.play( - t_tracker.set_value, 0, - run_time=2 - ) - plane.clear_updaters() - self.play(FadeOut(plane)) - - def ambiently_change_solution(self): - A_trackers = self.A_trackers - - def generate_A_updater(A, rate): - def update(m, dt): - m.total_time += dt - m.set_value( - 2 * A * np.sin(rate * m.total_time + PI / 6) - ) - return update - - rates = [0, 0.2] + [ - 0.5 + 0.5 * np.random.random() - for x in range(len(A_trackers) - 2) - ] - - for tracker, rate in zip(A_trackers, rates): - tracker.total_time = 0 - tracker.add_updater(generate_A_updater( - tracker.get_value(), - rate - )) - - self.add(*A_trackers) - self.surface_boundary_lines.resume_updating() - self.surface.resume_updating() - self.graph.resume_updating() - self.begin_ambient_camera_rotation(rate=0.01) - self.wait(30) - - -class AnalyzeSineCurve(TemperatureGraphScene): - CONFIG = { - "origin_point": 3 * LEFT, - "axes_config": { - "z_min": -1.5, - "z_max": 1.5, - "z_axis_config": { - "unit_size": 2, - "tick_frequency": 0.5, - } - }, - "tex_to_color_map": { - "{x}": GREEN, - "T": YELLOW, - "=": WHITE, - "0": WHITE, - "\\Delta t": WHITE, - "\\sin": WHITE, - "{t}": PINK, - } - } - - def construct(self): - self.setup_axes() - self.ask_about_sine_curve() - self.show_sine_wave_on_axes() - self.reference_curvature() - self.show_derivatives() - self.show_curvature_matching_height() - self.show_time_step_scalings() - self.smooth_evolution() - - def setup_axes(self): - axes = self.get_three_d_axes() - axes.rotate(90 * DEGREES, LEFT) - axes.shift(self.origin_point - axes.c2p(0, 0, 0)) - y_axis = axes.y_axis - y_axis.fade(1) - z_axis = axes.z_axis - z_axis.label.next_to(z_axis.get_end(), UP, SMALL_BUFF) - - self.add_axes_numbers(axes) - y_axis.remove(y_axis.numbers) - axes.z_axis.add_numbers( - *range(-1, 2), - direction=LEFT, - ) - - self.axes = axes - - def ask_about_sine_curve(self): - curve = FunctionGraph( - lambda t: np.sin(t), - x_min=0, - x_max=TAU, - ) - curve.move_to(DR) - curve.set_width(5) - curve.set_color(YELLOW) - question = TextMobject("What's so special?") - question.scale(1.5) - question.to_edge(UP) - question.shift(2 * LEFT) - arrow = Arrow( - question.get_bottom(), - curve.point_from_proportion(0.25) - ) - - self.play( - ShowCreation(curve), - Write(question, run_time=1), - GrowArrow(arrow), - ) - self.wait() - - self.quick_sine_curve = curve - self.question_group = VGroup(question, arrow) - - def show_sine_wave_on_axes(self): - axes = self.axes - graph = self.get_initial_state_graph( - axes, lambda x: np.sin(x) - ) - graph.set_stroke(width=4) - graph_label = TexMobject( - "T({x}, 0) = \\sin\\left({x}\\right)", - tex_to_color_map=self.tex_to_color_map, - ) - graph_label.next_to( - graph.point_from_proportion(0.25), UR, - buff=SMALL_BUFF, - ) - - v_line, x_tracker = self.get_v_line_with_x_tracker(graph) - - xs = VGroup( - *graph_label.get_parts_by_tex("x"), - axes.x_axis.label, - ) - - self.play( - Write(axes), - self.quick_sine_curve.become, graph, - FadeOutAndShift(self.question_group, UP), - ) - self.play( - FadeInFromDown(graph_label), - FadeIn(graph), - ) - self.remove(self.quick_sine_curve) - self.add(v_line) - self.play( - ApplyMethod( - x_tracker.set_value, TAU, - rate_func=lambda t: smooth(t, 3), - run_time=5, - ), - LaggedStartMap( - ShowCreationThenFadeAround, xs, - run_time=3, - lag_ratio=0.2, - ) - ) - self.remove(v_line, x_tracker) - self.wait() - - self.graph = graph - self.graph_label = graph_label - self.v_line = v_line - self.x_tracker = x_tracker - - def reference_curvature(self): - curve_segment, curve_x_tracker = \ - self.get_curve_segment_with_x_tracker(self.graph) - - self.add(curve_segment) - self.play( - curve_x_tracker.set_value, TAU, - run_time=5, - rate_func=lambda t: smooth(t, 3), - ) - self.play(FadeOut(curve_segment)) - - self.curve_segment = curve_segment - self.curve_x_tracker = curve_x_tracker - - def show_derivatives(self): - deriv1 = TexMobject( - "{\\partial T \\over \\partial {x}}({x}, 0)", - "= \\cos\\left({x}\\right)", - tex_to_color_map=self.tex_to_color_map, - ) - deriv2 = TexMobject( - "{\\partial^2 T \\over \\partial {x}^2}({x}, 0)", - "= -\\sin\\left({x}\\right)", - tex_to_color_map=self.tex_to_color_map, - ) - - deriv1.to_corner(UR) - deriv2.next_to( - deriv1, DOWN, - buff=0.75, - aligned_edge=LEFT, - ) - VGroup(deriv1, deriv2).shift(1.4 * RIGHT) - - self.play( - Animation(Group(*self.get_mobjects())), - FadeInFrom(deriv1, LEFT), - self.camera.frame_center.shift, 2 * RIGHT, - ) - self.wait() - self.play( - FadeInFrom(deriv2, UP) - ) - self.wait() - - self.deriv1 = deriv1 - self.deriv2 = deriv2 - - def show_curvature_matching_height(self): - axes = self.axes - graph = self.graph - curve_segment = self.curve_segment - curve_x_tracker = self.curve_x_tracker - - d2_graph = self.get_initial_state_graph( - axes, lambda x: -np.sin(x), - ) - dashed_d2_graph = DashedVMobject(d2_graph, num_dashes=50) - dashed_d2_graph.color_using_background_image(None) - dashed_d2_graph.set_stroke(RED, 2) - - vector, x_tracker = self.get_v_line_with_x_tracker( - d2_graph, - line_creator=lambda p1, p2: Arrow( - p1, p2, color=RED, buff=0 - ) - ) - - lil_vectors = self.get_many_lil_vectors(graph) - lil_vector = always_redraw( - lambda: self.get_lil_vector( - graph, x_tracker.get_value() - ) - ) - - d2_rect = SurroundingRectangle( - self.deriv2[-5:], - color=RED, - ) - self.play(ShowCreation(d2_rect)) - self.add(vector) - self.add(lil_vector) - self.add(curve_segment) - curve_x_tracker.set_value(0) - self.play( - ShowCreation(dashed_d2_graph), - x_tracker.set_value, TAU, - curve_x_tracker.set_value, TAU, - ShowIncreasingSubsets(lil_vectors[1:]), - run_time=8, - rate_func=linear, - ) - self.remove(vector) - self.remove(lil_vector) - self.add(lil_vectors) - self.play( - FadeOut(curve_segment), - FadeOut(d2_rect), - ) - - self.lil_vectors = lil_vectors - self.dashed_d2_graph = dashed_d2_graph - - def show_time_step_scalings(self): - axes = self.axes - graph_label = self.graph_label - dashed_d2_graph = self.dashed_d2_graph - lil_vectors = self.lil_vectors - graph = self.graph - - factor = 0.9 - - new_label = TexMobject( - "T({x}, \\Delta t) = c \\cdot \\sin\\left({x}\\right)", - tex_to_color_map=self.tex_to_color_map, - ) - final_label = TexMobject( - "T({x}, {t}) = (\\text{something}) \\cdot \\sin\\left({x}\\right)", - tex_to_color_map=self.tex_to_color_map, - ) - for label in (new_label, final_label): - label.shift( - graph_label.get_part_by_tex("=").get_center() - - label.get_part_by_tex("=").get_center() - ) - final_label.shift(1.5 * LEFT) - - h_lines = VGroup( - DashedLine(axes.c2p(0, 0, 1), axes.c2p(TAU, 0, 1)), - DashedLine(axes.c2p(0, 0, -1), axes.c2p(TAU, 0, -1)), - ) - - lil_vectors.add_updater(lambda m: m.become( - self.get_many_lil_vectors(graph) - )) - - i = 4 - self.play( - ReplacementTransform( - graph_label[:i], new_label[:i], - ), - ReplacementTransform( - graph_label[i + 1:i + 3], - new_label[i + 1:i + 3], - ), - FadeOutAndShift(graph_label[i], UP), - FadeInFrom(new_label[i], DOWN), - ) - self.play( - ReplacementTransform( - graph_label[i + 3:], - new_label[i + 4:] - ), - FadeInFromDown(new_label[i + 3]) - ) - self.play( - FadeOut(dashed_d2_graph), - FadeIn(h_lines), - ) - self.play( - graph.stretch, factor, 1, - h_lines.stretch, factor, 1, - ) - self.wait() - - # Repeat - last_coef = None - last_exp = None - delta_T = new_label.get_part_by_tex("\\Delta t") - c = new_label.get_part_by_tex("c")[0] - prefix = new_label[:4] - prefix.generate_target() - for x in range(5): - coef = Integer(x + 2) - exp = coef.copy().scale(0.7) - coef.next_to( - delta_T, LEFT, SMALL_BUFF, - aligned_edge=DOWN, - ) - exp.move_to(c.get_corner(UR), DL) - anims1 = [FadeInFrom(coef, 0.25 * DOWN)] - anims2 = [FadeInFrom(exp, 0.25 * DOWN)] - if last_coef: - anims1.append( - FadeOutAndShift(last_coef, 0.25 * UP) - ) - anims2.append( - FadeOutAndShift(last_exp, 0.25 * UP) - ) - last_coef = coef - last_exp = exp - prefix.target.next_to(coef, LEFT, SMALL_BUFF) - prefix.target.match_y(prefix) - anims1.append(MoveToTarget(prefix)) - - self.play(*anims1) - self.play( - graph.stretch, factor, 1, - h_lines.stretch, factor, 1, - *anims2, - ) - self.play( - ReplacementTransform( - new_label[:4], - final_label[:4], - ), - ReplacementTransform( - VGroup(last_coef, delta_T), - final_label.get_part_by_tex("{t}"), - ), - ReplacementTransform( - last_exp, - final_label.get_part_by_tex("something"), - ), - FadeOut(new_label.get_part_by_tex("\\cdot"), UP), - ReplacementTransform( - new_label[-4:], - final_label[-4:], - ), - ReplacementTransform( - new_label.get_part_by_tex("="), - final_label.get_part_by_tex("="), - ), - ReplacementTransform( - new_label.get_part_by_tex(")"), - final_label.get_part_by_tex(")"), - ), - ) - final_label.add_background_rectangle(opacity=1) - self.add(final_label) - self.wait() - - group = VGroup(graph, h_lines) - group.add_updater(lambda m, dt: m.stretch( - (1 - 0.1 * dt), 1 - )) - self.add(group) - self.wait(10) - - def smooth_evolution(self): - pass - - # - def get_rod(self, temp_func): - pass - - def get_v_line_with_x_tracker(self, graph, line_creator=DashedLine): - axes = self.axes - x_min = axes.x_axis.p2n(graph.get_start()) - x_max = axes.x_axis.p2n(graph.get_end()) - x_tracker = ValueTracker(x_min) - get_x = x_tracker.get_value - v_line = always_redraw(lambda: line_creator( - axes.c2p(get_x(), 0, 0), - graph.point_from_proportion( - inverse_interpolate( - x_min, x_max, get_x() - ) - ), - )) - return v_line, x_tracker - - def get_curve_segment_with_x_tracker(self, graph, delta_x=0.5): - axes = self.axes - x_min = axes.x_axis.p2n(graph.get_start()) - x_max = axes.x_axis.p2n(graph.get_end()) - x_tracker = ValueTracker(x_min) - get_x = x_tracker.get_value - - def x2a(x): - return inverse_interpolate(x_min, x_max, x) - - curve = VMobject( - stroke_color=WHITE, - stroke_width=5 - ) - curve.add_updater(lambda m: m.pointwise_become_partial( - graph, - max(x2a(get_x() - delta_x), 0), - min(x2a(get_x() + delta_x), 1), - )) - return curve, x_tracker - - def get_lil_vector(self, graph, x): - x_axis = self.axes.x_axis - point = graph.point_from_proportion(x / TAU) - x_axis_point = x_axis.n2p(x_axis.p2n(point)) - return Arrow( - point, - interpolate( - point, x_axis_point, 0.5, - ), - buff=0, - color=RED - ) - - def get_many_lil_vectors(self, graph, n=13): - return VGroup(*[ - self.get_lil_vector(graph, x) - for x in np.linspace(0, TAU, n) - ]) - - -class SineWaveScaledByExp(TemperatureGraphScene): - CONFIG = { - "axes_config": { - "z_min": -1.5, - "z_max": 1.5, - "z_axis_config": { - "unit_size": 2, - "tick_frequency": 0.5, - "label_direction": LEFT, - }, - "y_axis_config": { - "label_direction": RIGHT, - }, - }, - "k": 0.3, - } - - def construct(self): - self.setup_axes() - self.setup_camera() - self.show_sine_wave() - self.show_decay_surface() - self.linger_at_end() - - def setup_axes(self): - axes = self.get_three_d_axes() - - # Add number labels - self.add_axes_numbers(axes) - for axis in [axes.x_axis, axes.y_axis]: - axis.numbers.rotate( - 90 * DEGREES, - axis=axis.get_vector(), - about_point=axis.point_from_proportion(0.5) - ) - axis.numbers.set_shade_in_3d(True) - axes.z_axis.add_numbers(*range(-1, 2)) - for number in axes.z_axis.numbers: - number.rotate(90 * DEGREES, RIGHT) - - axes.z_axis.label.next_to( - axes.z_axis.get_end(), OUT, - ) - - # Input plane - axes.input_plane.set_opacity(0.25) - self.add(axes.input_plane) - - # Shift into place - # axes.shift(5 * LEFT) - self.axes = axes - self.add(axes) - - def setup_camera(self): - self.set_camera_orientation( - phi=80 * DEGREES, - theta=-80 * DEGREES, - distance=50, - ) - self.camera.set_frame_center( - 2 * RIGHT, - ) - - def show_sine_wave(self): - time_tracker = ValueTracker(0) - graph = always_redraw( - lambda: self.get_time_slice_graph( - self.axes, - self.sin_exp, - t=time_tracker.get_value(), - ) - ) - graph.suspend_updating() - - graph_label = TexMobject("\\sin(x)") - graph_label.set_color(BLUE) - graph_label.rotate(90 * DEGREES, RIGHT) - graph_label.next_to( - graph.point_from_proportion(0.25), - OUT, - SMALL_BUFF, - ) - - self.play( - ShowCreation(graph), - FadeInFrom(graph_label, IN) - ) - self.wait() - graph.resume_updating() - - self.time_tracker = time_tracker - self.graph = graph - - def show_decay_surface(self): - time_tracker = self.time_tracker - axes = self.axes - - plane = Rectangle() - plane.rotate(90 * DEGREES, RIGHT) - plane.set_stroke(width=0) - plane.set_fill(WHITE, 0.2) - plane.match_depth(axes.z_axis) - plane.match_width(axes.x_axis, stretch=True) - plane.add_updater( - lambda p: p.move_to(axes.c2p( - 0, - time_tracker.get_value(), - 0, - ), LEFT) - ) - - time_slices = VGroup(*[ - self.get_time_slice_graph( - self.axes, - self.sin_exp, - t=t, - ) - for t in range(0, 10) - ]) - surface_t_tracker = ValueTracker(0) - surface = always_redraw( - lambda: self.get_surface( - self.axes, - self.sin_exp, - v_max=surface_t_tracker.get_value(), - ).set_stroke(opacity=0) - ) - - exp_graph = ParametricFunction( - lambda t: axes.c2p( - PI / 2, - t, - self.sin_exp(PI / 2, t) - ), - t_min=axes.y_min, - t_max=axes.y_max, - ) - exp_graph.set_stroke(RED, 3) - exp_graph.set_shade_in_3d(True) - - exp_label = TexMobject("e^{-\\alpha t}") - exp_label.scale(1.5) - exp_label.set_color(RED) - exp_label.rotate(90 * DEGREES, RIGHT) - exp_label.rotate(90 * DEGREES, OUT) - exp_label.next_to( - exp_graph.point_from_proportion(0.3), - OUT + UP, - ) - - self.move_camera( - theta=-25 * DEGREES, - ) - self.add(surface) - self.add(plane) - self.play( - surface_t_tracker.set_value, axes.y_max, - time_tracker.set_value, axes.y_max, - ShowIncreasingSubsets( - time_slices, - int_func=np.ceil, - ), - run_time=5, - rate_func=linear, - ) - surface.clear_updaters() - - self.play( - ShowCreation(exp_graph), - FadeOut(plane), - FadeInFrom(exp_label, IN), - time_slices.set_stroke, {"width": 1}, - ) - - def linger_at_end(self): - self.wait() - self.begin_ambient_camera_rotation(rate=-0.02) - self.wait(20) - - # - def sin_exp(self, x, t): - return np.sin(x) * np.exp(-self.k * t) - - -class BoundaryConditionReference(ShowEvolvingTempGraphWithArrows): - def construct(self): - self.setup_axes() - self.setup_graph() - - rod = self.get_rod(0, 10) - self.color_rod_by_graph(rod) - - boundary_points = [ - rod.get_right(), - rod.get_left(), - ] - boundary_dots = VGroup(*[ - Dot(point, radius=0.2) - for point in boundary_points - ]) - boundary_arrows = VGroup(*[ - Vector(2 * DOWN).next_to(dot, UP) - for dot in boundary_dots - ]) - boundary_arrows.set_stroke(YELLOW, 10) - - words = TextMobject( - "Different rules\\\\", - "at the boundary", - ) - words.scale(1.5) - words.to_edge(UP) - - # self.add(self.axes) - # self.add(self.graph) - self.add(rod) - self.play(FadeInFromDown(words)) - self.play( - LaggedStartMap(GrowArrow, boundary_arrows), - LaggedStartMap(GrowFromCenter, boundary_dots), - lag_ratio=0.3, - run_time=1, - ) - self.wait() - - -class SimulateRealSineCurve(ShowEvolvingTempGraphWithArrows): - CONFIG = { - "axes_config": { - "x_min": 0, - "x_max": TAU, - "x_axis_config": { - "unit_size": 1.5, - "include_tip": False, - "tick_frequency": PI / 4, - }, - "y_min": -1.5, - "y_max": 1.5, - "y_axis_config": { - "tick_frequency": 0.5, - "unit_size": 2, - }, - }, - "graph_x_min": 0, - "graph_x_max": TAU, - "arrow_xs": np.linspace(0, TAU, 13), - "rod_opacity": 0.5, - "wait_time": 30, - "alpha": 0.5, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_clock() - self.add_rod() - self.add_arrows() - self.initialize_updaters() - self.let_play() - - def add_labels_to_axes(self): - x_axis = self.axes.x_axis - x_axis.add(*[ - TexMobject(tex).scale(0.5).next_to( - x_axis.n2p(n), - DOWN, - buff=MED_SMALL_BUFF - ) - for tex, n in [ - ("\\tau \\over 4", TAU / 4), - ("\\tau \\over 2", TAU / 2), - ("3 \\tau \\over 4", 3 * TAU / 4), - ("\\tau", TAU), - ] - ]) - - def add_axes(self): - super().add_axes() - self.add_labels_to_axes() - - def add_rod(self): - super().add_rod() - self.rod.set_opacity(self.rod_opacity) - self.rod.set_stroke(width=0) - - def initial_function(self, x): - return np.sin(x) - - def y_to_color(self, y): - return temperature_to_color(0.8 * y) - - -class StraightLine3DGraph(TemperatureGraphScene): - CONFIG = { - "axes_config": { - "z_min": 0, - "z_max": 10, - "z_axis_config": { - 'unit_size': 0.5, - } - }, - "c": 1.2, - } - - def construct(self): - axes = self.get_three_d_axes() - axes.add(axes.input_plane) - axes.move_to(2 * IN + UP, IN) - surface = self.get_surface( - axes, self.func, - ) - initial_graph = self.get_initial_state_graph( - axes, lambda x: self.func(x, 0) - ) - - plane = self.get_const_time_plane(axes) - initial_graph.add_updater( - lambda m: m.move_to(plane, IN) - ) - - self.set_camera_orientation( - phi=80 * DEGREES, - theta=-100 * DEGREES, - ) - self.begin_ambient_camera_rotation() - - self.add(axes) - self.add(initial_graph) - self.play( - TransformFromCopy(initial_graph, surface) - ) - self.add(surface, initial_graph) - self.wait() - - self.play( - FadeIn(plane), - ApplyMethod( - plane.t_tracker.set_value, 10, - rate_func=linear, - run_time=10, - ) - ) - self.play(FadeOut(plane)) - self.wait(15) - - def func(self, x, t): - return self.c * x - - -class SimulateLinearGraph(SimulateRealSineCurve): - CONFIG = { - "axes_config": { - "y_min": 0, - "y_max": 3, - "y_axis_config": { - "tick_frequency": 0.5, - "unit_size": 2, - }, - }, - "arrow_scale_factor": 2, - "alpha": 1, - "wait_time": 40, - "step_size": 0.02, - } - - # def let_play(self): - # pass - - def add_labels_to_axes(self): - pass - - def y_to_color(self, y): - return temperature_to_color(0.8 * (y - 1.5)) - - def initial_function(self, x): - axes = self.axes - y_max = axes.y_max - x_max = axes.x_max - slope = y_max / x_max - return slope * x - - -class EmphasizeBoundaryPoints(SimulateLinearGraph): - CONFIG = { - "wait_time": 30, - } - - def let_play(self): - rod = self.rod - self.graph.update(0.01) - self.arrows.update() - - to_update = VGroup( - self.graph, - self.arrows, - self.time_label, - ) - for mob in to_update: - mob.suspend_updating() - - dots = VGroup( - Dot(rod.get_left()), - Dot(rod.get_right()), - ) - for dot in dots: - dot.set_height(0.2) - dot.set_color(YELLOW) - - words = TextMobject( - "Different rules\\\\" - "at the boundary" - ) - words.next_to(rod, UP) - - arrows = VGroup(*[ - Arrow( - words.get_corner(corner), - dot.get_top(), - path_arc=u * 60 * DEGREES, - ) - for corner, dot, u in zip( - [LEFT, RIGHT], dots, [1, -1] - ) - ]) - - self.add(words) - self.play( - LaggedStartMap( - FadeInFromLarge, dots, - scale_factor=5, - ), - LaggedStartMap( - ShowCreation, arrows, - ), - lag_ratio=0.4 - ) - self.wait() - - for mob in to_update: - mob.resume_updating() - - super().let_play() - - -class FlatEdgesContinuousEvolution(ShowEvolvingTempGraphWithArrows): - CONFIG = { - "wait_time": 30, - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - ], - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_clock() - self.add_rod() - self.initialize_updaters() - self.add_boundary_lines() - self.let_play() - - def add_boundary_lines(self): - - lines = VGroup(*[ - Line(LEFT, RIGHT) - for x in range(2) - ]) - lines.set_width(1.5) - lines.set_stroke(WHITE, 5, opacity=0.5) - lines.add_updater(self.update_lines) - - turn_animation_into_updater( - ShowCreation(lines, run_time=2) - ) - self.add(lines) - - def update_lines(self, lines): - graph = self.graph - lines[0].move_to(graph.get_start()) - lines[1].move_to(graph.get_end()) - - def initial_function(self, x): - return ShowEvolvingTempGraphWithArrows.initial_function(self, x) - - -class MoreAccurateTempFlowWithArrows(ShowEvolvingTempGraphWithArrows): - CONFIG = { - "arrow_scale_factor": 1, - "max_magnitude": 1, - "freq_amplitude_pairs": [ - (1, 0.5), - (2, 1), - (3, 0.5), - (4, 0.3), - ], - } - - def setup_axes(self): - super().setup_axes() - y_axis = self.axes.y_axis - y_axis.remove(y_axis.label) - - -class SlopeToHeatFlow(FlatEdgesContinuousEvolution): - CONFIG = { - "wait_time": 20, - "xs": [1.75, 5.25, 7.8], - "axes_config": { - "x_min": 0, - "x_max": 10, - "x_axis_config": { - "include_tip": False, - } - }, - "n_arrows": 10, - "random_seed": 1, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_rod() - # - self.show_slope_labels() - self.show_heat_flow() - - def show_slope_labels(self): - axes = self.axes - # axes.x_axis.add_numbers() - graph = self.graph - xs = self.xs - - lines = VGroup(*[ - TangentLine( - graph, - inverse_interpolate( - axes.x_min, - axes.x_max, - x - ), - length=2, - stroke_opacity=0.75, - ) - for x in xs - ]) - - slope_words = VGroup() - for line in lines: - slope = line.get_slope() - word = TextMobject( - "Slope =", - "${:.2}$".format(slope) - ) - if slope > 0: - word[1].set_color(GREEN) - else: - word[1].set_color(RED) - - word.set_width(line.get_length()) - word.next_to(ORIGIN, UP, SMALL_BUFF) - word.rotate( - line.get_angle(), - about_point=ORIGIN, - ) - word.shift(line.get_center()) - slope_words.add(word) - - self.play( - LaggedStartMap(ShowCreation, lines), - LaggedStartMap(Write, slope_words), - lag_ratio=0.5, - run_time=1, - ) - self.wait() - - self.lines = lines - self.slope_words = slope_words - - def show_heat_flow(self): - axes = self.axes - xs = self.xs - slope_words = self.slope_words - tan_lines = self.lines - - slopes = map(Line.get_slope, self.lines) - flow_rates = [-s for s in slopes] - - points = list(map(axes.x_axis.n2p, xs)) - v_lines = VGroup(*[ - Line( - line.get_center(), point, - stroke_width=1, - ) - for point, line in zip(points, tan_lines) - ]) - - flow_words = VGroup(*[ - TextMobject("Heat flow").next_to( - point, DOWN - ).scale(0.8) - for point in points - ]) - flow_mobjects = VGroup(*[ - self.get_flow(x, flow_rate) - for x, flow_rate in zip(xs, flow_rates) - ]) - - self.add(flow_mobjects) - self.wait() - self.play( - LaggedStart(*[ - TransformFromCopy( - sw[0], fw[0] - ) - for sw, fw in zip(slope_words, flow_words) - ]), - LaggedStartMap(ShowCreation, v_lines), - lag_ratio=0.4, - run_time=2, - ) - self.wait(self.wait_time) - - def get_flow(self, x, flow_rate): - return VGroup(*[ - self.get_single_flow_arrow( - x, flow_rate, - t_offset=to - ) - for to in np.linspace(0, 5, self.n_arrows) - ]) - - def get_single_flow_arrow(self, x, flow_rate, t_offset): - axes = self.axes - point = axes.x_axis.n2p(x) - - h_shift = 0.5 - v_shift = 0.4 * np.random.random() + 0.1 - - arrow = Vector(0.5 * RIGHT * np.sign(flow_rate)) - arrow.move_to(point) - arrow.shift(v_shift * UP) - lp = arrow.get_center() + h_shift * LEFT - rp = arrow.get_center() + h_shift * RIGHT - lc = self.rod_point_to_color(lp) - rc = self.rod_point_to_color(rp) - - run_time = 3 / flow_rate - animation = UpdateFromAlphaFunc( - arrow, - lambda m, a: m.move_to( - interpolate(lp, rp, a) - ).set_color( - interpolate_color(lc, rc, a) - ).set_opacity(there_and_back(a)), - run_time=run_time, - ) - result = cycle_animation(animation) - animation.total_time += t_offset - return result - - -class CloserLookAtStraightLine(SimulateLinearGraph): - def construct(self): - self.add_axes() - self.add_graph() - self.add_clock() - self.add_rod() - # self.initialize_updaters() - # - self.show_t_eq_0_state() - self.show_t_gt_0_state() - - def show_t_eq_0_state(self): - t_eq = TexMobject("t", "=", "0") - t_eq.next_to(self.time_label, DOWN) - - circles = VGroup(*[ - Circle( - radius=0.25, - stroke_color=YELLOW, - ) - for x in range(2) - ]) - circles.add_updater(self.attach_to_endpoints) - - self.play(Write(t_eq)) - self.play(ShowCreation(circles)) - self.wait() - self.play(FadeOut(circles)) - - self.circles = circles - self.t_eq = t_eq - - def show_t_gt_0_state(self): - # circles = self.circles - t_eq = self.t_eq - - t_ineq = TexMobject("t", ">", "0") - t_ineq.move_to(t_eq) - - slope_lines = VGroup(*[ - Line(LEFT, RIGHT) - for x in range(2) - ]) - slope_lines.set_opacity(0.5) - slope_lines.add_updater(self.attach_to_endpoints) - - self.remove(t_eq) - self.add(t_ineq) - - self.initialize_updaters() - self.run_clock(0.1) - for mob in self.mobjects: - mob.suspend_updating() - self.wait() - - self.add(slope_lines) - self.add(self.clock, self.time_label, t_ineq) - self.play(ShowCreation(slope_lines)) - for mob in self.mobjects: - mob.resume_updating() - - self.run_clock(self.wait_time) - - # - def attach_to_endpoints(self, mobs): - points = [ - self.graph.get_start(), - self.graph.get_end(), - ] - for mob, point in zip(mobs, points): - mob.move_to(point) - return mobs - - -class ManipulateSinExpSurface(TemperatureGraphScene): - CONFIG = { - "axes_config": { - "z_max": 1.25, - "z_min": -1.25, - "z_axis_config": { - "unit_size": 2.5, - "tick_frequency": 0.5, - }, - "x_axis_config": { - # "unit_size": 1.5, - "unit_size": 1.0, - "tick_frequency": 1, - }, - "x_max": 10, - "y_max": 15, - }, - "alpha": 0.2, - "tex_mobject_config": { - "tex_to_color_map": { - "{x}": GREEN, - "{t}": YELLOW, - "\\omega": MAROON_B, - "^2": WHITE, - }, - }, - "graph_config": {}, - "initial_phi": -90 * DEGREES, - "initial_omega": 1, - } - - def construct(self): - self.setup_axes() - self.add_sine_wave() - self.shift_sine_to_cosine() - self.show_derivatives_of_cos() - self.show_cos_exp_surface() - self.change_frequency() - self.talk_through_omega() - self.show_cos_omega_derivatives() - self.show_rebalanced_exp() - - def setup_axes(self): - axes = self.get_three_d_axes(include_numbers=True) - axes.x_axis.remove(axes.x_axis.numbers) - L = TexMobject("L") - L.rotate(90 * DEGREES, RIGHT) - L.next_to(axes.x_axis.get_end(), IN) - axes.x_axis.label = L - axes.x_axis.add(L) - - axes.shift(5 * LEFT + 0.5 * IN) - axes.z_axis.label[0].remove( - *axes.z_axis.label[0][1:] - ) - axes.z_axis.label.next_to( - axes.z_axis.get_end(), OUT - ) - axes.z_axis.add_numbers( - *np.arange(-1, 1.5, 0.5), - direction=LEFT, - number_config={ - "num_decimal_places": 1, - } - ) - for number in axes.z_axis.numbers: - number.rotate(90 * DEGREES, RIGHT) - - self.set_camera_orientation( - phi=90 * DEGREES, - theta=-90 * DEGREES, - distance=100, - ) - - self.add(axes) - self.remove(axes.y_axis) - self.axes = axes - - def add_sine_wave(self): - self.initialize_parameter_trackers() - graph = self.get_graph() - sin_label = TexMobject( - "\\sin\\left({x}\\right)", - **self.tex_mobject_config, - ) - sin_label.shift(2 * LEFT + 2.75 * UP) - - self.add_fixed_in_frame_mobjects(sin_label) - graph.suspend_updating() - self.play( - ShowCreation(graph), - Write(sin_label), - ) - graph.resume_updating() - self.wait() - - self.sin_label = sin_label - self.graph = graph - - def shift_sine_to_cosine(self): - graph = self.graph - sin_label = self.sin_label - - sin_cross = Cross(sin_label) - sin_cross.add_updater( - lambda m: m.move_to(sin_label) - ) - cos_label = TexMobject( - "\\cos\\left({x}\\right)", - **self.tex_mobject_config, - ) - cos_label.move_to(sin_label, LEFT) - cos_label.shift(LEFT) - # cos_label.shift( - # axes.c2p(0, 0) - axes.c2p(PI / 2, 0), - # ) - - left_tangent = Line(ORIGIN, RIGHT) - left_tangent.set_stroke(WHITE, 5) - - self.add_fixed_in_frame_mobjects(cos_label) - self.play( - ApplyMethod( - self.phi_tracker.set_value, 0, - ), - FadeOutAndShift(sin_label, LEFT), - FadeInFrom(cos_label, RIGHT), - run_time=2, - ) - left_tangent.move_to(graph.get_start(), LEFT) - self.play(ShowCreation(left_tangent)) - self.play(FadeOut(left_tangent)) - - self.cos_label = cos_label - - def show_derivatives_of_cos(self): - cos_label = self.cos_label - cos_exp_label = TexMobject( - "\\cos\\left({x}\\right)", - "e^{-\\alpha {t}}", - **self.tex_mobject_config - ) - cos_exp_label.move_to(cos_label, LEFT) - - ddx_group, ddx_exp_group = [ - self.get_ddx_computation_group( - label, - *[ - TexMobject( - s + "\\left({x}\\right)" + exp, - **self.tex_mobject_config, - ) - for s in ["-\\sin", "-\\cos"] - ] - ) - for label, exp in [ - (cos_label, ""), - (cos_exp_label, "e^{-\\alpha {t}}"), - ] - ] - - self.add_fixed_in_frame_mobjects(ddx_group) - self.play(FadeIn(ddx_group)) - self.wait() - - # Cos exp - transforms = [ - ReplacementTransform( - cos_label, cos_exp_label, - ), - ReplacementTransform( - ddx_group, ddx_exp_group, - ), - ] - for trans in transforms: - trans.begin() - self.add_fixed_in_frame_mobjects(trans.mobject) - self.play(*transforms) - self.add_fixed_in_frame_mobjects( - cos_exp_label, ddx_exp_group - ) - self.remove_fixed_in_frame_mobjects( - cos_label, - *[ - trans.mobject - for trans in transforms - ] - ) - - self.cos_exp_label = cos_exp_label - self.ddx_exp_group = ddx_exp_group - - def show_cos_exp_surface(self): - axes = self.axes - - surface = self.get_cos_exp_surface() - self.add(surface) - surface.max_t_tracker.set_value(0) - self.move_camera( - phi=85 * DEGREES, - theta=-80 * DEGREES, - added_anims=[ - ApplyMethod( - surface.max_t_tracker.set_value, - axes.y_max, - run_time=4, - ), - Write(axes.y_axis), - ] - ) - self.wait() - - self.surface = surface - - def change_frequency(self): - cos_exp_label = self.cos_exp_label - ddx_exp_group = self.ddx_exp_group - omega_tracker = self.omega_tracker - - cos_omega = TexMobject( - "\\cos\\left(", - "\\omega", "\\cdot", "{x}", - "\\right)", - **self.tex_mobject_config - ) - cos_omega.move_to(cos_exp_label, LEFT) - - omega = cos_omega.get_part_by_tex("\\omega") - brace = Brace(omega, UP, buff=SMALL_BUFF) - omega_decimal = always_redraw( - lambda: DecimalNumber( - omega_tracker.get_value(), - color=omega.get_color(), - ).next_to(brace, UP, SMALL_BUFF) - ) - - self.add_fixed_in_frame_mobjects( - cos_omega, - brace, - omega_decimal, - ) - self.play( - self.camera.phi_tracker.set_value, 90 * DEGREES, - self.camera.theta_tracker.set_value, -90 * DEGREES, - FadeOut(self.surface), - FadeOut(self.axes.y_axis), - FadeOut(cos_exp_label), - FadeOut(ddx_exp_group), - FadeIn(cos_omega), - GrowFromCenter(brace), - FadeInFromDown(omega_decimal) - ) - for n in [2, 6, 1, 4]: - freq = n * PI / self.axes.x_max - self.play( - omega_tracker.set_value, freq, - run_time=2 - ) - self.wait() - self.wait() - - self.cos_omega = cos_omega - self.omega_brace = brace - - def talk_through_omega(self): - axes = self.axes - - x_tracker = ValueTracker(0) - get_x = x_tracker.get_value - v_line = always_redraw(lambda: DashedLine( - axes.c2p(get_x(), 0, 0), - axes.c2p(get_x(), 0, self.func(get_x(), 0)), - )) - - x = self.cos_omega.get_part_by_tex("{x}") - brace = Brace(x, DOWN) - x_decimal = always_redraw( - lambda: DecimalNumber( - get_x(), - color=x.get_color() - ).next_to(brace, DOWN, SMALL_BUFF) - ) - - self.add(v_line) - self.add_fixed_in_frame_mobjects(brace, x_decimal) - self.play( - x_tracker.set_value, 5, - run_time=5, - rate_func=linear, - ) - self.play( - FadeOut(v_line), - FadeOut(brace), - FadeOut(x_decimal), - ) - self.remove_fixed_in_frame_mobjects( - brace, x_decimal - ) - - def show_cos_omega_derivatives(self): - cos_omega = self.cos_omega - ddx_omega_group = self.get_ddx_computation_group( - cos_omega, - *[ - TexMobject( - s + "\\left(\\omega \\cdot {x}\\right)", - **self.tex_mobject_config, - ) - for s in ["-\\omega \\sin", "-\\omega^2 \\cos"] - ] - ) - - omega_squared = ddx_omega_group[-1][1:3] - rect = SurroundingRectangle(omega_squared) - - self.add_fixed_in_frame_mobjects(ddx_omega_group) - self.play(FadeIn(ddx_omega_group)) - self.wait() - self.add_fixed_in_frame_mobjects(rect) - self.play(ShowCreationThenFadeOut(rect)) - self.wait() - - self.ddx_omega_group = ddx_omega_group - - def show_rebalanced_exp(self): - cos_omega = self.cos_omega - ddx_omega_group = self.ddx_omega_group - - cos_exp = TexMobject( - "\\cos\\left(", - "\\omega", "\\cdot", "{x}", - "\\right)", - "e^{-\\alpha \\omega^2 {t}}", - **self.tex_mobject_config - ) - cos_exp.move_to(cos_omega, DL) - - self.add_fixed_in_frame_mobjects(cos_exp) - self.play( - FadeOut(cos_omega), - FadeOut(ddx_omega_group), - FadeIn(cos_exp), - ) - self.remove_fixed_in_frame_mobjects( - cos_omega, - ddx_omega_group, - ) - - self.play( - FadeIn(self.surface), - FadeIn(self.axes.y_axis), - VGroup( - cos_exp, - self.omega_brace, - ).shift, 4 * RIGHT, - self.camera.phi_tracker.set_value, 80 * DEGREES, - self.camera.theta_tracker.set_value, -80 * DEGREES, - ) - self.wait() - self.play( - self.omega_tracker.set_value, TAU / 10, - run_time=6, - ) - - # - def initialize_parameter_trackers(self): - self.phi_tracker = ValueTracker( - self.initial_phi - ) - self.omega_tracker = ValueTracker( - self.initial_omega - ) - self.t_tracker = ValueTracker(0) - - def get_graph(self): - return always_redraw( - lambda: self.get_time_slice_graph( - self.axes, self.func, - t=self.t_tracker.get_value(), - **self.graph_config - ) - ) - - def get_cos_exp_surface(self): - max_t_tracker = ValueTracker(self.axes.y_max) - surface = always_redraw( - lambda: self.get_surface( - self.axes, - self.func, - v_max=max_t_tracker.get_value(), - ) - ) - surface.max_t_tracker = max_t_tracker - return surface - - def func(self, x, t): - phi = self.phi_tracker.get_value() - omega = self.omega_tracker.get_value() - alpha = self.alpha - return op.mul( - np.cos(omega * (x + phi)), - np.exp(-alpha * (omega**2) * t) - ) - - def get_ddx_computation_group(self, f, df, ddf): - arrows = VGroup(*[ - Vector(0.5 * RIGHT) for x in range(2) - ]) - group = VGroup( - arrows[0], df, arrows[1], ddf - ) - group.arrange(RIGHT) - group.next_to(f, RIGHT) - - for arrow in arrows: - label = TexMobject( - "\\partial \\over \\partial {x}", - **self.tex_mobject_config, - ) - label.scale(0.5) - label.next_to(arrow, UP, SMALL_BUFF) - arrow.add(label) - - group.arrows = arrows - group.funcs = VGroup(df, ddf) - - return group - - -class ShowFreq1CosExpDecay(ManipulateSinExpSurface): - CONFIG = { - "freq": 1, - "alpha": 0.2, - } - - def construct(self): - self.setup_axes() - self.add_back_y_axis() - self.initialize_parameter_trackers() - self.phi_tracker.set_value(0) - self.omega_tracker.set_value( - self.freq * TAU / 10, - ) - # - self.show_decay() - - def add_back_y_axis(self): - axes = self.axes - self.add(axes.y_axis) - self.remove(axes.y_axis.numbers) - self.remove(axes.y_axis.label) - - def show_decay(self): - axes = self.axes - t_tracker = self.t_tracker - t_max = self.axes.y_max - - graph = always_redraw( - lambda: self.get_time_slice_graph( - axes, - self.func, - t=t_tracker.get_value(), - stroke_width=5, - ) - ) - - surface = self.get_surface( - self.axes, self.func, - ) - plane = self.get_const_time_plane(axes) - - self.add(surface, plane, graph) - - self.set_camera_orientation( - phi=80 * DEGREES, - theta=-85 * DEGREES, - ) - self.begin_ambient_camera_rotation(rate=0.01) - self.play( - t_tracker.set_value, t_max, - plane.t_tracker.set_value, t_max, - run_time=t_max, - rate_func=linear, - ) - - -class ShowFreq2CosExpDecay(ShowFreq1CosExpDecay): - CONFIG = { - "freq": 2, - } - - -class ShowFreq4CosExpDecay(ShowFreq1CosExpDecay): - CONFIG = { - "freq": 4, - } - - -class ShowHarmonics(SimulateRealSineCurve): - CONFIG = { - "rod_opacity": 0.75, - "initial_omega": 1.27, - "default_n_rod_pieces": 32, - } - - def construct(self): - self.add_axes() - self.add_graph() - self.add_rod() - self.rod.add_updater(self.color_rod_by_graph) - # - self.show_formula() - - def add_graph(self): - omega_tracker = ValueTracker(self.initial_omega) - get_omega = omega_tracker.get_value - graph = always_redraw( - lambda: self.axes.get_graph( - lambda x: np.cos(get_omega() * x), - x_min=self.graph_x_min, - x_max=self.graph_x_max, - step_size=self.step_size, - discontinuities=[5], - ).color_using_background_image("VerticalTempGradient") - ) - - self.add(graph) - self.graph = graph - self.omega_tracker = omega_tracker - - def show_formula(self): - rod = self.rod - graph = self.graph - axes = self.axes - omega_tracker = self.omega_tracker - L = TAU - - formula = TexMobject( - "=", "\\cos\\left(", - "2(\\pi / L)", "\\cdot", "{x}", - "\\right)", - tex_to_color_map={ - "{x}": GREEN, - } - ) - formula.next_to( - self.axes.y_axis.label, RIGHT, SMALL_BUFF - ) - omega_part = formula.get_part_by_tex("\\pi") - omega_brace = Brace(omega_part, DOWN) - omega = TexMobject("\\omega") - omega.set_color(MAROON_B) - omega.next_to(omega_brace, DOWN, SMALL_BUFF) - formula.remove(omega_part) - - pi_over_L = TexMobject("(\\pi / L)") - pi_over_L.move_to(omega_part) - pi_over_L.match_color(omega) - - self.add(formula) - self.add(omega_brace) - self.add(omega) - - self.remove(graph) - self.play(GrowFromEdge(rod, LEFT)) - self.play( - ShowCreationThenFadeAround(axes.x_axis.label) - ) - self.wait() - self.play(FadeIn(graph)) - self.play(FadeInFromDown(pi_over_L)) - self.play( - omega_tracker.set_value, PI / L, - run_time=2 - ) - self.wait() - - # Show x - x_tracker = ValueTracker(0) - tip = ArrowTip( - start_angle=-90 * DEGREES, - color=WHITE, - ) - tip.add_updater(lambda m: m.move_to( - axes.x_axis.n2p(x_tracker.get_value()), - DOWN, - )) - x_sym = TexMobject("x") - x_sym.set_color(GREEN) - x_sym.add_background_rectangle(buff=SMALL_BUFF) - x_sym.add_updater(lambda m: m.next_to(tip, UP, SMALL_BUFF)) - - self.play( - Write(tip), - Write(x_sym), - ) - self.play( - x_tracker.set_value, L, - run_time=3, - ) - self.wait() - self.play(TransformFromCopy( - x_sym, formula.get_part_by_tex("{x}") - )) - self.play( - FadeOut(tip), - FadeOut(x_sym), - ) - - # Harmonics - pi_over_L.generate_target() - n_sym = Integer(2) - n_sym.match_color(pi_over_L) - group = VGroup(n_sym, pi_over_L.target) - group.arrange(RIGHT, buff=SMALL_BUFF) - group.move_to(pi_over_L) - - self.play( - MoveToTarget(pi_over_L), - FadeInFromDown(n_sym), - ApplyMethod( - omega_tracker.set_value, 2 * PI / L, - run_time=2, - ) - ) - self.wait() - for n in [*range(3, 9), 0]: - new_n_sym = Integer(n) - new_n_sym.move_to(n_sym, DR) - new_n_sym.match_style(n_sym) - self.play( - FadeOutAndShift(n_sym, UP), - FadeInFrom(new_n_sym, DOWN), - omega_tracker.set_value, n * PI / L, - ) - self.wait() - n_sym = new_n_sym - - # - def add_labels_to_axes(self): - x_axis = self.axes.x_axis - L = TexMobject("L") - L.next_to(x_axis.get_end(), DOWN) - x_axis.add(L) - x_axis.label = L - - -class ShowHarmonicSurfaces(ManipulateSinExpSurface): - CONFIG = { - "alpha": 0.2, - "initial_phi": 0, - "initial_omega": PI / 10, - "n_iterations": 8, - "default_surface_config": { - "resolution": (40, 30), - "surface_piece_config": { - "stroke_width": 0.5, - } - } - } - - def construct(self): - self.setup_axes() - self.initialize_parameter_trackers() - self.add_surface() - self.add_graph() - self.show_all_harmonic_surfaces() - - def setup_axes(self): - super().setup_axes() - self.add(self.axes.y_axis) - self.set_camera_orientation( - phi=82 * DEGREES, - theta=-80 * DEGREES, - ) - - def add_surface(self): - self.surface = self.get_cos_exp_surface() - self.add(self.surface) - - def add_graph(self): - self.graph = self.get_graph() - self.add(self.graph) - - def show_all_harmonic_surfaces(self): - omega_tracker = self.omega_tracker - formula = self.get_formula(str(1)) - L = self.axes.x_max - - self.begin_ambient_camera_rotation(rate=0.01) - self.add_fixed_in_frame_mobjects(formula) - self.wait(2) - for n in range(2, self.n_iterations): - if n > 5: - n_str = "n" - else: - n_str = str(n) - new_formula = self.get_formula(n_str) - self.play( - Transform(formula, new_formula), - ApplyMethod( - omega_tracker.set_value, - n * PI / L - ), - ) - self.wait(3) - - # - def get_formula(self, n_str): - n_str = "{" + n_str + "}" - result = TexMobject( - "\\cos\\left(", - n_str, "(", "\\pi / L", ")", "{x}" - "\\right)" - "e^{-\\alpha (", n_str, "\\pi / L", ")^2", - "{t}}", - tex_to_color_map={ - "{x}": GREEN, - "{t}": YELLOW, - "\\pi / L": MAROON_B, - n_str: MAROON_B, - } - ) - result.to_edge(UP) - return result - - -class Thumbnail(ShowHarmonicSurfaces): - CONFIG = { - "default_surface_config": { - "resolution": (40, 30), - # "resolution": (10, 10), - }, - "graph_config": { - "stroke_width": 8, - }, - } - - def construct(self): - self.setup_axes() - self.initialize_parameter_trackers() - self.add_surface() - self.add_graph() - # - self.omega_tracker.set_value(3 * PI / 10) - self.set_camera_orientation( - theta=-70 * DEGREES, - ) - - axes = self.axes - for axis in [axes.y_axis, axes.z_axis]: - axis.numbers.set_opacity(0) - axis.remove(*axis.numbers) - axes.x_axis.label.set_opacity(0) - axes.z_axis.label.set_opacity(0) - - for n in range(2, 16, 2): - new_graph = self.get_time_slice_graph( - axes, self.func, t=n, - **self.graph_config - ) - new_graph.set_shade_in_3d(True) - new_graph.set_stroke( - width=8 / np.sqrt(n), - # opacity=1 / n**(1 / 4), - ) - self.add(new_graph) - - words = TextMobject( - "Sine waves + Linearity + Fourier = Solution" - ) - words.set_width(FRAME_WIDTH - 1) - words.to_edge(DOWN) - words.shift(2 * DOWN) - self.add_fixed_in_frame_mobjects(words) - - self.camera.frame_center.shift(DOWN) - self.update_mobjects(0) - self.surface.set_stroke(width=0.1) - self.surface.set_fill(opacity=0.2) diff --git a/active_projects/ode/part3/wordy_scenes.py b/active_projects/ode/part3/wordy_scenes.py deleted file mode 100644 index e1160ff8..00000000 --- a/active_projects/ode/part3/wordy_scenes.py +++ /dev/null @@ -1,919 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part2.wordy_scenes import * - - -class ThreeMainObservations(Scene): - def construct(self): - fourier = ImageMobject("Joseph Fourier") - name = TextMobject("Joseph Fourier") - name.match_width(fourier) - name.next_to(fourier, DOWN, SMALL_BUFF) - fourier.add(name) - fourier.set_height(5) - fourier.to_corner(DR) - fourier.shift(LEFT) - bubble = ThoughtBubble( - direction=RIGHT, - height=3, - width=4, - ) - bubble.move_tip_to(fourier.get_corner(UL) + 0.5 * DR) - - observations = VGroup( - TextMobject( - "1)", - "Sine = Nice", - ), - TextMobject( - "2)", - "Linearity" - ), - TextMobject( - "3)", - "Fourier series" - ), - ) - # heart = SuitSymbol("hearts") - # heart.replace(observations[0][2]) - # observations[0][2].become(heart) - # observations[0][1].add(happiness) - # observations[2][2].align_to( - # observations[2][1], LEFT, - # ) - - observations.arrange( - DOWN, - aligned_edge=LEFT, - buff=2 * LARGE_BUFF, - ) - observations.set_height(FRAME_HEIGHT - 2) - observations.to_corner(UL, buff=LARGE_BUFF) - - self.add(fourier) - self.play(ShowCreation(bubble)) - self.wait() - self.play(LaggedStart(*[ - TransformFromCopy(bubble, observation[0]) - for observation in observations - ], lag_ratio=0.2)) - self.play( - FadeOut(fourier), - FadeOut(bubble), - ) - self.wait() - for obs in observations: - self.play(FadeInFrom(obs[1], LEFT)) - self.wait() - - -class LastChapterWrapper(Scene): - def construct(self): - full_rect = FullScreenFadeRectangle( - fill_color=DARK_GREY, - fill_opacity=1, - ) - rect = ScreenRectangle(height=6) - rect.set_stroke(WHITE, 2) - rect.set_fill(BLACK, 1) - title = TextMobject("Last chapter") - title.scale(2) - title.to_edge(UP) - rect.next_to(title, DOWN) - - self.add(full_rect) - self.play( - FadeIn(rect), - Write(title, run_time=2), - ) - self.wait() - - -class ThreeConstraints(WriteHeatEquationTemplate): - def construct(self): - self.cross_out_solving() - self.show_three_conditions() - - def cross_out_solving(self): - equation = self.get_d1_equation() - words = TextMobject("Solve this equation") - words.to_edge(UP) - equation.next_to(words, DOWN) - cross = Cross(words) - - self.add(words, equation) - self.wait() - self.play(ShowCreation(cross)) - self.wait() - - self.equation = equation - self.to_remove = VGroup(words, cross) - - def show_three_conditions(self): - equation = self.equation - to_remove = self.to_remove - - title = TexMobject( - "\\text{Constraints }" - "T({x}, {t})" - "\\text{ must satisfy:}", - **self.tex_mobject_config - ) - title.to_edge(UP) - - items = VGroup( - TextMobject("1)", "The PDE"), - TextMobject("2)", "Boundary condition"), - TextMobject("3)", "Initial condition"), - ) - items.scale(0.7) - items.arrange(RIGHT, buff=LARGE_BUFF) - items.set_width(FRAME_WIDTH - 2) - items.next_to(title, DOWN, LARGE_BUFF) - items[1].set_color(MAROON_B) - items[2].set_color(RED) - - bc_paren = TextMobject("(Explained soon)") - bc_paren.scale(0.7) - bc_paren.next_to(items[1], DOWN) - - self.play( - FadeInFromDown(title), - FadeOutAndShift(to_remove, UP), - equation.scale, 0.6, - equation.next_to, items[0], DOWN, - equation.shift_onto_screen, - LaggedStartMap(FadeIn, [ - items[0], - items[1][0], - items[2][0], - ]) - ) - self.wait() - self.play(Write(items[1][1])) - bc_paren.match_y(equation) - self.play(FadeInFrom(bc_paren, UP)) - self.wait(2) - self.play(Write(items[2][1])) - self.wait(2) - - self.title = title - self.items = items - self.pde = equation - self.bc_paren = bc_paren - - -class RectAroundEquation(WriteHeatEquationTemplate): - def construct(self): - eq = self.get_d1_equation() - self.play(ShowCreationThenFadeAround(eq)) - - -class BorderRect(Scene): - def construct(self): - rect = FullScreenFadeRectangle() - rect.set_stroke(WHITE, 3) - rect.set_fill(opacity=0) - self.add(rect) - - -class SeekIdealized(Scene): - def construct(self): - phrases = VGroup(*[ - TextMobject( - "Seek", text, "problems", - tex_to_color_map={ - "realistic": GREEN, - "{idealized}": YELLOW, - "over-idealized": YELLOW, - "general": BLUE, - } - ) - for text in [ - "realistic", - "{idealized}", - "over-idealized", - "general", - ] - ]) - phrases.scale(2) - words = VGroup() - for phrase in phrases: - phrase.center() - word = phrase[1] - words.add(word) - phrase.remove(word) - arrow = Vector(DOWN) - arrow.set_stroke(WHITE, 6) - arrow.next_to(words[3], UP) - low_arrow = arrow.copy() - low_arrow.next_to(words[3], DOWN) - - solutions = TextMobject("solutions") - solutions.scale(2) - solutions.move_to(phrases[3][1], UL) - models = TextMobject("models") - models.scale(2) - models.next_to( - words[0], RIGHT, buff=0.35, - aligned_edge=DOWN - ) - - phrases.center() - phrase = phrases[0] - - self.add(phrase) - self.add(words[0]) - self.wait() - words[0].save_state() - self.play( - words[0].to_edge, DOWN, - words[0].set_opacity, 0.5, - Transform(phrase, phrases[1]), - FadeInFrom(words[1], UP) - ) - self.wait() - # self.play( - # words[1].move_to, words[2], RIGHT, - # FadeIn(words[2]), - # Transform(phrase, phrases[2]) - # ) - # self.wait() - self.play( - words[1].next_to, arrow, UP, - ShowCreation(arrow), - MaintainPositionRelativeTo( - phrase, words[1] - ), - FadeInFrom(solutions, LEFT), - FadeIn(words[3]), - ) - self.wait() - - words[0].generate_target() - words[0].target.next_to(low_arrow, DOWN) - words[0].target.set_opacity(1) - models.shift( - words[0].target.get_center() - - words[0].saved_state.get_center() - ) - self.play( - MoveToTarget(words[0]), - ShowCreation(low_arrow), - FadeInFrom(models, LEFT) - ) - self.wait() - - -class SecondDerivativeOfSine(Scene): - def construct(self): - equation = TexMobject( - "{d^2 \\over d{x}^2}", - "\\cos\\left({x}\\right) =", - "-\\cos\\left({x}\\right)", - tex_to_color_map={ - "{x}": GREEN, - } - ) - - self.add(equation) - - -class EquationAboveSineAnalysis(WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d1_equation() - equation.to_edge(UP) - equation.shift(2 * LEFT) - eq_index = equation.index_of_part_by_tex("=") - lhs = equation[:eq_index] - eq = equation[eq_index] - rhs = equation[eq_index + 1:] - t_terms = equation.get_parts_by_tex("{t}")[1:] - t_terms.save_state() - zeros = VGroup(*[ - TexMobject("0").replace(t, dim_to_match=1) - for t in t_terms - ]) - zeros.align_to(t_terms, DOWN) - new_rhs = TexMobject( - "=", "-\\alpha \\cdot {T}", "({x}, 0)", - **self.tex_mobject_config - ) - # new_rhs.move_to(equation.get_right()) - # new_rhs.next_to(equation, DOWN, MED_LARGE_BUFF) - # new_rhs.align_to(eq, LEFT) - new_rhs.next_to(equation, RIGHT) - new_rhs.shift(SMALL_BUFF * DOWN) - - self.add(equation) - self.play(ShowCreationThenFadeAround(rhs)) - self.wait() - self.play( - FadeOutAndShift(t_terms, UP), - FadeInFrom(zeros, DOWN), - ) - t_terms.fade(1) - self.wait() - self.play( - # VGroup(equation, zeros).next_to, - # new_rhs, LEFT, - FadeIn(new_rhs), - ) - self.wait() - self.play( - VGroup( - lhs[6:], - eq, - rhs, - new_rhs[0], - new_rhs[-3:], - zeros, - ).fade, 0.5, - ) - self.play(ShowCreationThenFadeAround(lhs[:6])) - self.play(ShowCreationThenFadeAround(new_rhs[1:-3])) - self.wait() - - -class ExpVideoWrapper(Scene): - def construct(self): - self.add(FullScreenFadeRectangle( - fill_color=DARKER_GREY, - fill_opacity=1, - )) - - screen = ImageMobject("eoc_chapter5_thumbnail") - screen.set_height(6) - rect = SurroundingRectangle(screen, buff=0) - rect.set_stroke(WHITE, 2) - screen.add(rect) - title = TextMobject("Need a refresher?") - title.scale(1.5) - title.to_edge(UP) - screen.next_to(title, DOWN) - - screen.center() - self.play( - # FadeInFrom(title, LEFT), - FadeInFrom(screen, DOWN), - ) - self.wait() - - -class ShowSinExpDerivatives(WriteHeatEquationTemplate): - CONFIG = { - "tex_mobject_config": { - "tex_to_color_map": { - "{0}": WHITE, - "\\partial": WHITE, - "=": WHITE, - } - } - } - - def construct(self): - pde = self.get_d1_equation_without_inputs() - pde.to_edge(UP) - pde.generate_target() - - new_rhs = TexMobject( - "=- \\alpha \\cdot T", - **self.tex_mobject_config, - ) - new_rhs.next_to(pde, RIGHT) - new_rhs.align_to(pde.get_part_by_tex("alpha"), DOWN) - - equation1 = TexMobject( - "T({x}, {0}) = \\sin\\left({x}\\right)", - **self.tex_mobject_config - ) - equation2 = TexMobject( - "T({x}, {t}) = \\sin\\left({x}\\right)", - "e^{-\\alpha{t}}", - **self.tex_mobject_config - ) - for eq in equation1, equation2: - eq.next_to(pde, DOWN, MED_LARGE_BUFF) - - eq2_part1 = equation2[:len(equation1)] - eq2_part2 = equation2[len(equation1):] - - # Rectangles - exp_rect = SurroundingRectangle(eq2_part2) - exp_rect.set_stroke(RED, 3) - sin_rect = SurroundingRectangle( - eq2_part1[-3:] - ) - sin_rect.set_color(BLUE) - - VGroup(pde.target, new_rhs).center().to_edge(UP) - - # Show proposed solution - self.add(pde) - self.add(equation1) - self.wait() - self.play( - MoveToTarget(pde), - FadeInFrom(new_rhs, LEFT) - ) - self.wait() - self.play( - ReplacementTransform(equation1, eq2_part1), - FadeIn(eq2_part2), - ) - self.play(ShowCreation(exp_rect)) - self.wait() - self.play(FadeOut(exp_rect)) - - # Take partial derivatives wrt x - q_mark = TexMobject("?") - q_mark.next_to(pde.get_part_by_tex("="), UP) - q_mark.set_color(RED) - - arrow1 = Vector(3 * DOWN + 1 * RIGHT, color=WHITE) - arrow1.scale(1.2 / arrow1.get_length()) - arrow1.next_to( - eq2_part2.get_corner(DL), - DOWN, MED_LARGE_BUFF - ) - ddx_label1 = TexMobject( - "\\partial \\over \\partial {x}", - **self.tex_mobject_config, - ) - ddx_label1.scale(0.7) - ddx_label1.next_to( - arrow1.point_from_proportion(0.8), - UR, SMALL_BUFF - ) - - pde_ddx = VGroup( - *pde.get_parts_by_tex("\\partial")[2:], - pde.get_parts_by_tex("\\over")[1], - pde.get_part_by_tex("{x}"), - ) - pde_ddx_rect = SurroundingRectangle(pde_ddx) - pde_ddx_rect.set_color(GREEN) - - eq2_part2_rect = SurroundingRectangle(eq2_part2) - - dx = TexMobject( - "\\cos\\left({x}\\right)", "e^{-\\alpha {t}}", - **self.tex_mobject_config - ) - ddx = TexMobject( - "-\\sin\\left({x}\\right)", "e^{-\\alpha {t}}", - **self.tex_mobject_config - ) - dx.next_to(arrow1, DOWN) - dx.align_to(eq2_part2, RIGHT) - x_shift = arrow1.get_end()[0] - arrow1.get_start()[0] - x_shift *= 2 - dx.shift(x_shift * RIGHT) - arrow2 = arrow1.copy() - arrow2.next_to(dx, DOWN) - arrow2.shift(MED_SMALL_BUFF * RIGHT) - dx_arrows = VGroup(arrow1, arrow2) - - ddx_label2 = ddx_label1.copy() - ddx_label2.shift( - arrow2.get_center() - arrow1.get_center() - ) - ddx.next_to(arrow2, DOWN) - ddx.align_to(eq2_part2, RIGHT) - ddx.shift(2 * x_shift * RIGHT) - - rhs = equation2[-6:] - - self.play( - FadeInFromDown(q_mark) - ) - self.play( - ShowCreation(pde_ddx_rect) - ) - self.wait() - self.play( - LaggedStart( - GrowArrow(arrow1), - GrowArrow(arrow2), - ), - TransformFromCopy( - pde_ddx[0], ddx_label1 - ), - TransformFromCopy( - pde_ddx[0], ddx_label2 - ), - ) - self.wait() - self.play( - TransformFromCopy(rhs, dx) - ) - self.wait() - self.play( - FadeIn(eq2_part2_rect) - ) - self.play( - Transform( - eq2_part2_rect, - SurroundingRectangle(dx[-3:]) - ) - ) - self.play( - FadeOut(eq2_part2_rect) - ) - self.wait() - self.play( - TransformFromCopy(dx, ddx) - ) - self.play( - FadeIn( - SurroundingRectangle(ddx).match_style( - pde_ddx_rect - ) - ) - ) - self.wait() - - # Take partial derivative wrt t - pde_ddt = pde[:pde.index_of_part_by_tex("=") - 1] - pde_ddt_rect = SurroundingRectangle(pde_ddt) - - dt_arrow = Arrow( - arrow1.get_start(), - arrow2.get_end() + RIGHT, - buff=0 - ) - dt_arrow.flip(UP) - dt_arrow.next_to(dx_arrows, LEFT, MED_LARGE_BUFF) - - dt_label = TexMobject( - "\\partial \\over \\partial {t}", - **self.tex_mobject_config, - ) - dt_label.scale(1) - dt_label.next_to( - dt_arrow.get_center(), UL, - SMALL_BUFF, - ) - - rhs_copy = rhs.copy() - rhs_copy.next_to(dt_arrow.get_end(), DOWN) - rhs_copy.shift(MED_LARGE_BUFF * LEFT) - rhs_copy.match_y(ddx) - - minus_alpha_in_exp = rhs_copy[-3][1:].copy() - minus_alpha_in_exp.set_color(RED) - minus_alpha = TexMobject("-\\alpha") - minus_alpha.next_to(rhs_copy, LEFT) - minus_alpha.align_to(rhs_copy[0][0], DOWN) - dot = TexMobject("\\cdot") - dot.move_to(midpoint( - minus_alpha.get_right(), - rhs_copy.get_left(), - )) - - self.play( - TransformFromCopy( - pde_ddx_rect, - pde_ddt_rect, - ) - ) - self.play( - GrowArrow(dt_arrow), - TransformFromCopy( - pde_ddt, - dt_label, - ) - ) - self.wait() - self.play(TransformFromCopy(rhs, rhs_copy)) - self.play(FadeIn(minus_alpha_in_exp)) - self.play( - ApplyMethod( - minus_alpha_in_exp.replace, minus_alpha, - path_arc=TAU / 4 - ), - FadeIn(dot), - ) - self.play( - FadeIn(minus_alpha), - FadeOut(minus_alpha_in_exp), - ) - self.wait() - rhs_copy.add(minus_alpha, dot) - self.play( - FadeIn(SurroundingRectangle(rhs_copy)) - ) - self.wait() - - # - checkmark = TexMobject("\\checkmark") - checkmark.set_color(GREEN) - checkmark.move_to(q_mark, DOWN) - self.play( - FadeInFromDown(checkmark), - FadeOutAndShift(q_mark, UP) - ) - self.wait() - - -class DerivativesOfLinearFunction(WriteHeatEquationTemplate): - CONFIG = { - "tex_mobject_config": { - "tex_to_color_map": { - "{c}": WHITE, - } - } - } - - def construct(self): - func = TexMobject( - "T({x}, {t}) = {c} \\cdot {x}", - **self.tex_mobject_config - ) - dx_T = TexMobject("{c}", **self.tex_mobject_config) - ddx_T = TexMobject("0") - dt_T = TexMobject("0") - - for mob in func, dx_T, ddx_T, dt_T: - mob.scale(1.5) - - func.generate_target() - - arrows = VGroup(*[ - Vector(1.5 * RIGHT, color=WHITE) - for x in range(3) - ]) - dx_arrows = arrows[:2] - dt_arrow = arrows[2] - dt_arrow.rotate(-TAU / 4) - dx_group = VGroup( - func.target, - dx_arrows[0], - dx_T, - dx_arrows[1], - ddx_T, - ) - dx_group.arrange(RIGHT) - for arrow, char, vect in zip(arrows, "xxt", [UP, UP, RIGHT]): - label = TexMobject( - "\\partial \\over \\partial {%s}" % char, - **self.tex_mobject_config - ) - label.scale(0.7) - label.next_to(arrow.get_center(), vect) - arrow.add(label) - - dt_arrow.shift( - func.target[-3:].get_bottom() + MED_SMALL_BUFF * DOWN - - dt_arrow.get_start(), - ) - dt_T.next_to(dt_arrow.get_end(), DOWN) - - self.play(FadeInFromDown(func)) - self.wait() - self.play( - MoveToTarget(func), - LaggedStartMap(Write, dx_arrows), - run_time=1, - ) - self.play( - TransformFromCopy(func[-3:], dx_T), - path_arc=-TAU / 4, - ) - self.play( - TransformFromCopy(dx_T, ddx_T), - path_arc=-TAU / 4, - ) - self.wait() - - # dt - self.play(Write(dt_arrow)) - self.play( - TransformFromCopy(func[-3:], dt_T) - ) - self.wait() - - -class FlatAtBoundaryWords(Scene): - def construct(self): - words = self.get_bc_words() - self.play(Write(words)) - self.wait() - - def get_bc_words(self): - return TextMobject( - "Flat at boundary\\\\" - "for all", "${t}$", "$> 0$", - ) - - -class WriteOutBoundaryCondition(FlatAtBoundaryWords, ThreeConstraints, MovingCameraScene): - def construct(self): - self.force_skipping() - ThreeConstraints.construct(self) - self.revert_to_original_skipping_status() - - self.add_ic() - self.write_bc_words() - self.write_bc_equation() - - def add_ic(self): - image = ImageMobject("temp_initial_condition_example") - image.set_width(3) - border = SurroundingRectangle(image, buff=SMALL_BUFF) - border.shift(SMALL_BUFF * UP) - border.set_stroke(WHITE, 2) - group = Group(image, border) - group.next_to(self.items[2], DOWN) - self.add(group) - - def write_bc_words(self): - bc_paren = self.bc_paren - bc_words = self.get_bc_words() - bc_words.match_width(self.items[1][1]) - bc_words.move_to(bc_paren, UP) - bc_words.set_color_by_tex("{t}", YELLOW) - - self.play(ShowCreationThenFadeAround( - VGroup(self.items[0], self.pde) - )) - self.play( - FadeOutAndShift(bc_paren, UP), - FadeInFrom(bc_words, DOWN), - ) - self.wait() - - self.bc_words = bc_words - - def write_bc_equation(self): - bc_words = self.bc_words - - equation = TexMobject( - "{\\partial {T} \\over \\partial {x}}(0, {t}) = ", - "{\\partial {T} \\over \\partial {x}}(L, {t}) = ", - "0", - **self.tex_mobject_config, - ) - equation.next_to(bc_words, DOWN, MED_LARGE_BUFF) - - self.play( - self.camera_frame.shift, 0.8 * DOWN, - ) - self.play(FadeInFrom(equation, UP)) - self.wait() - - -class HeatEquationFrame(WriteHeatEquationTemplate): - def construct(self): - equation = self.get_d1_equation() - equation.to_edge(UP, buff=MED_SMALL_BUFF) - - ddx = equation[-11:] - dt = equation[:11] - - full_rect = FullScreenFadeRectangle( - fill_color=DARK_GREY, - fill_opacity=1, - ) - smaller_rect = ScreenRectangle( - height=6, - fill_color=BLACK, - fill_opacity=1, - stroke_color=WHITE, - stroke_width=2, - ) - smaller_rect.next_to(equation, DOWN) - - self.add(full_rect) - self.add(smaller_rect) - self.add(equation) - self.wait() - self.play(ShowCreationThenFadeAround( - ddx, - surrounding_rectangle_config={ - "stroke_color": GREEN, - } - )) - self.wait() - self.play(ShowCreationThenFadeAround(dt)) - self.wait() - - -class CompareFreqDecays1to2(Scene): - CONFIG = { - "freqs": [1, 2] - } - - def construct(self): - background = FullScreenFadeRectangle( - fill_color=DARKER_GREY, - fill_opacity=1, - ) - - screens = VGroup(*[ - ScreenRectangle( - height=4, - fill_color=BLACK, - fill_opacity=1, - stroke_width=1, - stroke_color=WHITE, - ) - for x in range(2) - ]) - screens.arrange(RIGHT) - screens.set_width(FRAME_WIDTH - 1) - - formulas = VGroup(*[ - self.get_formula(freq) - for freq in self.freqs - ]) - for formula, screen in zip(formulas, screens): - formula.next_to(screen, UP) - - self.add(background) - self.add(screens) - self.add(formulas) - self.wait() - - def get_formula(self, freq): - f_str = str(freq) - return TexMobject( - "\\cos\\left(%s \\cdot {x}\\right)" % f_str, - "e^{-\\alpha \\cdot %s^2 \\cdot {t}}" % f_str, - tex_to_color_map={ - "{x}": GREEN, - "{t}": YELLOW, - f_str: MAROON_B, - } - ) - - -class CompareFreqDecays1to4(CompareFreqDecays1to2): - CONFIG = { - "freqs": [1, 4], - } - - -class CompareFreqDecays2to4(CompareFreqDecays1to2): - CONFIG = { - "freqs": [2, 4], - } - - -class WorryAboutGenerality(TeacherStudentsScene, WriteHeatEquationTemplate): - def construct(self): - eq = self.get_d1_equation() - diffyq = self.get_diffyq_set() - is_in = TexMobject("\\in") - is_in.scale(2) - - group = VGroup(eq, is_in, diffyq) - group.arrange(RIGHT, buff=MED_LARGE_BUFF) - group.to_edge(UP) - - arrow = Vector(DOWN) - arrow.set_stroke(WHITE, 5) - arrow.next_to(eq, DOWN) - themes = TextMobject("Frequent themes") - themes.scale(1.5) - themes.next_to(arrow, DOWN) - - self.play( - self.get_student_changes( - "sad", "tired", "pleading" - ), - self.teacher.change, "raise_right_hand", - FadeInFromDown(eq) - ) - self.play(Write(group[1:])) - self.wait(2) - self.play( - ShowCreation(arrow), - self.get_student_changes(*3 * ["pondering"]), - ) - self.play( - FadeInFrom(themes, UP), - self.get_student_changes(*3 * ["thinking"]), - self.teacher.change, "happy" - ) - self.wait(4) - - - # def get_d1_equation(self): - # result = super().get_d1_equation() - # lp, rp = parens = TexMobject("(", ")") - # parens.match_height(result) - # lp.next_to(result, LEFT, SMALL_BUFF) - # rp.next_to(result, RIGHT, SMALL_BUFF) - # result.add_to_back(lp) - # result.add(rp) - # return result - - def get_diffyq_set(self): - words = TextMobject( - "Differential\\\\equations" - ) - words.scale(1.5) - words.set_color(BLUE) - lb = Brace(words, LEFT) - rb = Brace(words, RIGHT) - return VGroup(lb, words, rb) diff --git a/active_projects/ode/part4/fourier_series_scenes.py b/active_projects/ode/part4/fourier_series_scenes.py deleted file mode 100644 index d2ea6fee..00000000 --- a/active_projects/ode/part4/fourier_series_scenes.py +++ /dev/null @@ -1,242 +0,0 @@ -from manimlib.imports import * - -from active_projects.ode.part2.fourier_series import FourierOfTrebleClef - - -class ComplexFourierSeriesExample(FourierOfTrebleClef): - CONFIG = { - "file_name": "EighthNote", - "run_time": 10, - "n_vectors": 200, - "n_cycles": 2, - "max_circle_stroke_width": 0.75, - "drawing_height": 5, - "center_point": DOWN, - "top_row_y": 3, - "top_row_label_y": 2, - "top_row_x_spacing": 1.75, - "top_row_copy_scale_factor": 0.9, - "start_drawn": False, - } - - def construct(self): - self.add_vectors_circles_path() - self.add_top_row(self.vectors, self.circles) - self.write_title() - self.highlight_vectors_one_by_one() - self.change_shape() - - def write_title(self): - title = TextMobject("Complex\\\\Fourier series") - title.scale(1.5) - title.to_edge(LEFT) - title.match_y(self.path) - - self.wait(5) - self.play(FadeInFromDown(title)) - self.wait(2) - self.title = title - - def highlight_vectors_one_by_one(self): - # Don't know why these vectors can't get copied. - # That seems like a problem that will come up again. - labels = self.top_row[-1] - next_anims = [] - for vector, circle, label in zip(self.vectors, self.circles, labels): - # v_color = vector.get_color() - c_color = circle.get_color() - c_stroke_width = circle.get_stroke_width() - - rect = SurroundingRectangle(label, color=PINK) - self.play( - # vector.set_color, PINK, - circle.set_stroke, RED, 3, - FadeIn(rect), - *next_anims - ) - self.wait() - next_anims = [ - # vector.set_color, v_color, - circle.set_stroke, c_color, c_stroke_width, - FadeOut(rect), - ] - self.play(*next_anims) - - def change_shape(self): - # path_mob = TexMobject("\\pi") - path_mob = SVGMobject("Nail_And_Gear") - new_path = path_mob.family_members_with_points()[0] - new_path.set_height(4) - new_path.move_to(self.path, DOWN) - new_path.shift(0.5 * UP) - new_coefs = self.get_coefficients_of_path(new_path) - new_vectors = self.get_rotating_vectors( - coefficients=new_coefs - ) - new_drawn_path = self.get_drawn_path(new_vectors) - - self.vector_clock.set_value(0) - self.vector_clock.suspend_updating(0) - - vectors = self.vectors - anims = [] - - for vect, new_vect in zip(vectors, new_vectors): - new_vect.update() - new_vect.clear_updaters() - - line = Line(stroke_width=0) - line.put_start_and_end_on(*vect.get_start_and_end()) - anims.append(ApplyMethod( - line.put_start_and_end_on, - *new_vect.get_start_and_end() - )) - vect.freq = new_vect.freq - vect.phase = new_vect.phase - vect.coefficient = new_vect.coefficient - - vect.line = line - vect.add_updater( - lambda v: v.put_start_and_end_on( - *v.line.get_start_and_end() - ) - ) - anims += [ - FadeOut(self.drawn_path) - ] - - self.play(*anims, run_time=3) - self.vector_clock.resume_updating() - for vect in self.vectors: - vect.remove_updater(vect.updaters[-1]) - - self.add(new_drawn_path) - for n in range(self.n_cycles): - self.run_one_cycle() - - # - def get_path(self): - path = super().get_path() - path.set_height(self.drawing_height) - path.to_edge(DOWN) - return path - - def add_top_row(self, vectors, circles, max_freq=3): - self.top_row = self.get_top_row( - vectors, circles, max_freq - ) - self.add(self.top_row) - - def get_top_row(self, vectors, circles, max_freq=3): - vector_copies = VGroup() - circle_copies = VGroup() - for vector, circle in zip(vectors, circles): - if vector.freq > max_freq: - break - vcopy = vector.copy() - vcopy.clear_updaters() - ccopy = circle.copy() - ccopy.clear_updaters() - ccopy.original = circle - vcopy.original = vector - - vcopy.center_point = np.array([ - vector.freq * self.top_row_x_spacing, - self.top_row_y, - 0 - ]) - ccopy.center_point = vcopy.center_point - vcopy.add_updater(self.update_top_row_vector_copy) - ccopy.add_updater(self.update_top_row_circle_copy) - vector_copies.add(vcopy) - circle_copies.add(ccopy) - - dots = VGroup(*[ - TexMobject("\\dots").next_to( - circle_copies, direction, - MED_LARGE_BUFF, - ) - for direction in [LEFT, RIGHT] - ]) - labels = self.get_top_row_labels(vector_copies) - return VGroup( - vector_copies, - circle_copies, - dots, - labels, - ) - - def update_top_row_vector_copy(self, vcopy): - vcopy.become(vcopy.original) - vcopy.scale(self.top_row_copy_scale_factor) - vcopy.shift(vcopy.center_point - vcopy.get_start()) - return vcopy - - def update_top_row_circle_copy(self, ccopy): - ccopy.become(ccopy.original) - ccopy.scale(self.top_row_copy_scale_factor) - ccopy.move_to(ccopy.center_point) - return ccopy - - def get_top_row_labels(self, vector_copies): - labels = VGroup() - for vector_copy in vector_copies: - freq = vector_copy.freq - label = Integer(freq) - label.move_to(np.array([ - freq * self.top_row_x_spacing, - self.top_row_label_y, - 0 - ])) - labels.add(label) - return labels - - -class ComplexFourierSeriesExampleEnd(ExternallyAnimatedScene): - pass - - -class FourierSeriesExampleWithRectForZoom(ComplexFourierSeriesExample): - CONFIG = { - "n_vectors": 100, - "slow_factor": 0.01, - "rect_scale_factor": 0.15, - "parametric_function_step_size": 0.0001, - "start_drawn": True, - } - - def construct(self): - self.add_vectors_circles_path() - self.circles.set_stroke(opacity=0.5) - rect = self.get_rect() - rect.set_height(self.rect_scale_factor * FRAME_HEIGHT) - rect.add_updater(lambda m: m.move_to( - center_of_mass([ - v.get_end() - for v in self.vectors - ]) - )) - self.add(rect) - self.run_one_cycle() - - def get_rect(self): - return ScreenRectangle( - color=WHITE, - stroke_width=2, - ) - - -class ZoomedInFourierSeriesExample(FourierSeriesExampleWithRectForZoom, MovingCameraScene): - CONFIG = { - "vector_config": { - "max_tip_length_to_length_ratio": 0.15, - "tip_length": 0.05, - } - } - - def setup(self): - ComplexFourierSeriesExample.setup(self) - MovingCameraScene.setup(self) - - def get_rect(self): - return self.camera_frame diff --git a/active_projects/ode/part4/pi_creature_scenes.py b/active_projects/ode/part4/pi_creature_scenes.py deleted file mode 100644 index 69ef9d32..00000000 --- a/active_projects/ode/part4/pi_creature_scenes.py +++ /dev/null @@ -1,22 +0,0 @@ -from manimlib.imports import * - - -class WhyWouldYouCare(TeacherStudentsScene): - def construct(self): - self.student_says( - "Who cares!", - target_mode="sassy", - student_index=2, - added_anims=[self.teacher.change, "guilty"], - ) - self.wait() - self.play( - RemovePiCreatureBubble(self.students[2]), - self.teacher.change, "raise_right_hand", - self.get_student_changes( - "pondering", "erm", "thinking", - look_at_arg=self.screen, - ) - ) - self.look_at(self.screen) - self.wait(5) diff --git a/active_projects/ode/part4/staging.py b/active_projects/ode/part4/staging.py deleted file mode 100644 index 0e16ca07..00000000 --- a/active_projects/ode/part4/staging.py +++ /dev/null @@ -1,345 +0,0 @@ -from manimlib.imports import * -from active_projects.ode.part3.temperature_graphs import TemperatureGraphScene -from active_projects.ode.part2.wordy_scenes import WriteHeatEquationTemplate - - -class RelationToOtherVideos(Scene): - CONFIG = { - "camera_config": { - "background_color": DARK_GREY, - }, - } - - def construct(self): - # Show three videos - videos = self.get_video_thumbnails() - brace = Brace(videos, UP) - text = TextMobject("Heat equation") - text.scale(2) - text.next_to(brace, UP) - - self.play( - LaggedStartMap( - FadeInFrom, videos, - lambda m: (m, LEFT), - lag_ratio=0.4, - run_time=2, - ), - GrowFromCenter(brace), - FadeInFromDown(text), - ) - self.wait() - group = Group(text, brace, videos) - - # Show Fourier thinking - fourier = ImageMobject("Joseph Fourier") - fourier.set_height(4) - fourier.to_edge(RIGHT) - group.generate_target() - group.target.to_edge(DOWN) - fourier.align_to(group.target[0], DOWN) - bubble = ThoughtBubble( - direction=RIGHT, - width=3, - height=2, - fill_opacity=0.5, - stroke_color=WHITE, - ) - bubble[-1].shift(0.25 * DOWN + 0.5 * LEFT) - bubble[:-1].rotate(20 * DEGREES) - for mob in bubble[:-1]: - mob.rotate(-20 * DEGREES) - bubble.move_tip_to( - fourier.get_corner(UL) + DOWN - ) - bubble.to_edge(UP, buff=SMALL_BUFF) - - self.play( - MoveToTarget(group), - FadeInFrom(fourier, LEFT) - ) - self.play(Write(bubble, run_time=1)) - self.wait() - - # Discount first two - first_two = videos[:2] - first_two.generate_target() - first_two.target.scale(0.5) - first_two.target.to_corner(DL) - new_brace = Brace(first_two.target, UP) - - self.play( - # fourier.scale, 0.8, - fourier.match_x, new_brace, - fourier.to_edge, UP, - MoveToTarget(first_two), - Transform(brace, new_brace), - text.scale, 0.7, - text.next_to, new_brace, UP, - FadeOutAndShift(bubble, LEFT), - ) - self.play( - videos[2].scale, 1.7, - videos[2].to_corner, UR, - ) - self.wait() - - # - def get_video_thumbnails(self): - thumbnails = Group( - ImageMobject("diffyq_part2_thumbnail"), - ImageMobject("diffyq_part3_thumbnail"), - ImageMobject("diffyq_part4_thumbnail"), - ) - for thumbnail in thumbnails: - thumbnail.set_height(4) - thumbnail.add(SurroundingRectangle( - thumbnail, - color=WHITE, - stroke_width=2, - buff=0 - )) - thumbnails.arrange(RIGHT, buff=LARGE_BUFF) - thumbnails.set_width(FRAME_WIDTH - 1) - return thumbnails - - -class ShowLinearity(WriteHeatEquationTemplate, TemperatureGraphScene): - CONFIG = { - "temp_text": "Temp", - "alpha": 0.1, - "axes_config": { - "z_max": 2, - "z_min": -2, - "z_axis_config": { - "tick_frequency": 0.5, - "unit_size": 1.5, - }, - }, - "default_surface_config": { - "resolution": (16, 16) - # "resolution": (4, 4) - }, - "freqs": [2, 5], - } - - def setup(self): - TemperatureGraphScene.setup(self) - WriteHeatEquationTemplate.setup(self) - - def construct(self): - self.init_camera() - self.add_three_graphs() - self.show_words() - self.add_function_labels() - self.change_scalars() - - def init_camera(self): - self.camera.set_distance(1000) - - def add_three_graphs(self): - axes_group = self.get_axes_group() - axes0, axes1, axes2 = axes_group - freqs = self.freqs - scalar_trackers = Group( - ValueTracker(1), - ValueTracker(1), - ) - graphs = VGroup( - self.get_graph(axes0, [freqs[0]], [scalar_trackers[0]]), - self.get_graph(axes1, [freqs[1]], [scalar_trackers[1]]), - self.get_graph(axes2, freqs, scalar_trackers), - ) - - plus = TexMobject("+").scale(2) - equals = TexMobject("=").scale(2) - plus.move_to(midpoint( - axes0.get_right(), - axes1.get_left(), - )) - equals.move_to(midpoint( - axes1.get_right(), - axes2.get_left(), - )) - - self.add(axes_group) - self.add(graphs) - self.add(plus) - self.add(equals) - - self.axes_group = axes_group - self.graphs = graphs - self.scalar_trackers = scalar_trackers - self.plus = plus - self.equals = equals - - def show_words(self): - equation = self.get_d1_equation() - name = TextMobject("Heat equation") - name.next_to(equation, DOWN) - name.set_color_by_gradient(RED, YELLOW) - group = VGroup(equation, name) - group.to_edge(UP) - - shift_val = 0.5 * RIGHT - - arrow = Vector(1.5 * RIGHT) - arrow.move_to(group) - arrow.shift(shift_val) - linear_word = TextMobject("``Linear''") - linear_word.scale(2) - linear_word.next_to(arrow, RIGHT) - - self.add(group) - self.wait() - self.play( - ShowCreation(arrow), - group.next_to, arrow, LEFT - ) - self.play(FadeInFrom(linear_word, LEFT)) - self.wait() - - def add_function_labels(self): - axes_group = self.axes_group - graphs = self.graphs - - solution_labels = VGroup() - for axes in axes_group: - label = TextMobject("Solution", "$\\checkmark$") - label.set_color_by_tex("checkmark", GREEN) - label.next_to(axes, DOWN) - solution_labels.add(label) - - kw = { - "tex_to_color_map": { - "T_1": BLUE, - "T_2": GREEN, - } - } - T1 = TexMobject("a", "T_1", **kw) - T2 = TexMobject("b", "T_2", **kw) - T_sum = TexMobject("T_1", "+", "T_2", **kw) - T_sum_with_scalars = TexMobject( - "a", "T_1", "+", "b", "T_2", **kw - ) - - T1.next_to(graphs[0], UP) - T2.next_to(graphs[1], UP) - T_sum.next_to(graphs[2], UP) - T_sum.shift(SMALL_BUFF * DOWN) - T_sum_with_scalars.move_to(T_sum) - - a_brace = Brace(T1[0], UP, buff=SMALL_BUFF) - b_brace = Brace(T2[0], UP, buff=SMALL_BUFF) - s1_decimal = DecimalNumber() - s1_decimal.match_color(T1[1]) - s1_decimal.next_to(a_brace, UP, SMALL_BUFF) - s1_decimal.add_updater(lambda m: m.set_value( - self.scalar_trackers[0].get_value() - )) - s2_decimal = DecimalNumber() - s2_decimal.match_color(T2[1]) - s2_decimal.next_to(b_brace, UP, SMALL_BUFF) - s2_decimal.add_updater(lambda m: m.set_value( - self.scalar_trackers[1].get_value() - )) - - self.play( - FadeInFrom(T1[1], DOWN), - FadeInFrom(solution_labels[0], UP), - ) - self.play( - FadeInFrom(T2[1], DOWN), - FadeInFrom(solution_labels[1], UP), - ) - self.wait() - self.play( - TransformFromCopy(T1[1], T_sum[0]), - TransformFromCopy(T2[1], T_sum[2]), - TransformFromCopy(self.plus, T_sum[1]), - *[ - Transform( - graph.copy().set_fill(opacity=0), - graphs[2].copy().set_fill(opacity=0), - remover=True - ) - for graph in graphs[:2] - ] - ) - self.wait() - self.play(FadeInFrom(solution_labels[2], UP)) - self.wait() - - # Show constants - self.play( - FadeIn(T1[0]), - FadeIn(T2[0]), - FadeIn(a_brace), - FadeIn(b_brace), - FadeIn(s1_decimal), - FadeIn(s2_decimal), - FadeOut(T_sum), - FadeIn(T_sum_with_scalars), - ) - - def change_scalars(self): - s1, s2 = self.scalar_trackers - - kw = { - "run_time": 2, - } - for graph in self.graphs: - graph.resume_updating() - self.play(s2.set_value, -0.5, **kw) - self.play(s1.set_value, -0.2, **kw) - self.play(s2.set_value, 1.5, **kw) - self.play(s1.set_value, 1.2) - self.play(s2.set_value, 0.3) - self.wait() - - # - def get_axes_group(self): - axes_group = VGroup(*[ - self.get_axes() - for x in range(3) - ]) - axes_group.arrange(RIGHT, buff=2) - axes_group.set_width(FRAME_WIDTH - 1) - axes_group.to_edge(DOWN, buff=1) - return axes_group - - def get_axes(self): - axes = self.get_three_d_axes() - # axes.input_plane.set_fill(opacity=0) - # axes.input_plane.set_stroke(width=0.5) - # axes.add(axes.input_plane) - self.orient_three_d_mobject(axes) - axes.rotate(-5 * DEGREES, UP) - axes.set_width(4) - axes.x_axis.label.next_to( - axes.x_axis.get_end(), DOWN, - buff=2 * SMALL_BUFF - ) - return axes - - def get_graph(self, axes, freqs, scalar_trackers): - L = axes.x_max - a = self.alpha - - def func(x, t): - scalars = [st.get_value() for st in scalar_trackers] - return np.sum([ - s * np.cos(k * x) * np.exp(-a * (k**2) * t) - for freq, s in zip(freqs, scalars) - for k in [freq * PI / L] - ]) - - def get_surface_graph_group(): - return VGroup( - self.get_surface(axes, func), - self.get_time_slice_graph(axes, func, t=0), - ) - - result = always_redraw(get_surface_graph_group) - result.suspend_updating() - return result diff --git a/active_projects/ode/solve_pendulum_ode_sample_code.py b/active_projects/ode/solve_pendulum_ode_sample_code.py deleted file mode 100644 index e7076c3c..00000000 --- a/active_projects/ode/solve_pendulum_ode_sample_code.py +++ /dev/null @@ -1,63 +0,0 @@ -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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -