mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 21:44:19 +08:00
Latest changes for animations of video 4 of diffyq series
This commit is contained in:
@ -4,7 +4,7 @@ from active_projects.diffyq.part1.pi_scenes import *
|
|||||||
from active_projects.diffyq.part1.phase_space import *
|
from active_projects.diffyq.part1.phase_space import *
|
||||||
from active_projects.diffyq.part1.wordy_scenes import *
|
from active_projects.diffyq.part1.wordy_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part1"
|
OUTPUT_DIRECTORY = "diffyq/part1"
|
||||||
SCENES_IN_ORDER = [
|
SCENES_IN_ORDER = [
|
||||||
WhenChangeIsEasier,
|
WhenChangeIsEasier,
|
||||||
VectorFieldTest,
|
VectorFieldTest,
|
||||||
|
@ -4,7 +4,7 @@ from active_projects.diffyq.part2.heat_equation import *
|
|||||||
from active_projects.diffyq.part2.pi_scenes import *
|
from active_projects.diffyq.part2.pi_scenes import *
|
||||||
from active_projects.diffyq.part2.wordy_scenes import *
|
from active_projects.diffyq.part2.wordy_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part2"
|
OUTPUT_DIRECTORY = "diffyq/part2"
|
||||||
SCENES_IN_ORDER = [
|
SCENES_IN_ORDER = [
|
||||||
PartTwoOfTour,
|
PartTwoOfTour,
|
||||||
HeatEquationIntroTitle,
|
HeatEquationIntroTitle,
|
||||||
|
@ -5,7 +5,7 @@ from active_projects.diffyq.part3.wordy_scenes import *
|
|||||||
from active_projects.diffyq.part3.discrete_case import *
|
from active_projects.diffyq.part3.discrete_case import *
|
||||||
|
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part3"
|
OUTPUT_DIRECTORY = "diffyq/part3"
|
||||||
SCENES_IN_ORDER = [
|
SCENES_IN_ORDER = [
|
||||||
LastChapterWrapper,
|
LastChapterWrapper,
|
||||||
ThreeConstraints,
|
ThreeConstraints,
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
from active_projects.diffyq.part4.staging import *
|
from active_projects.diffyq.part4.staging import *
|
||||||
from active_projects.diffyq.part4.fourier_series_scenes import *
|
from active_projects.diffyq.part4.fourier_series_scenes import *
|
||||||
from active_projects.diffyq.part4.pi_creature_scenes import *
|
from active_projects.diffyq.part4.pi_creature_scenes import *
|
||||||
|
from active_projects.diffyq.part4.three_d_graphs import *
|
||||||
|
from active_projects.diffyq.part4.temperature_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part4"
|
OUTPUT_DIRECTORY = "diffyq/part4"
|
||||||
SCENES_IN_ORDER = [
|
SCENES_IN_ORDER = [
|
||||||
ComplexFourierSeriesExample,
|
ComplexFourierSeriesExample,
|
||||||
ComplexFourierSeriesExampleEnd,
|
ComplexFourierSeriesExampleEnd,
|
||||||
FourierSeriesExampleWithRectForZoom,
|
FourierSeriesExampleWithRectForZoom,
|
||||||
ZoomedInFourierSeriesExample,
|
ZoomedInFourierSeriesExample,
|
||||||
|
ZoomedInFourierSeriesExample10xMore,
|
||||||
RelationToOtherVideos,
|
RelationToOtherVideos,
|
||||||
WhyWouldYouCare,
|
WhyWouldYouCare,
|
||||||
|
ShowLinearity,
|
||||||
|
CombineSeveralSolutions,
|
||||||
|
FourierGainsImmortality,
|
||||||
|
CycleThroughManyLinearCombinations,
|
||||||
|
StepFunctionExample,
|
||||||
|
WhichWavesAreAvailable,
|
||||||
# Oldies
|
# Oldies
|
||||||
|
|
||||||
# FourierSeriesIllustraiton,
|
# FourierSeriesIllustraiton,
|
||||||
|
@ -155,6 +155,9 @@ class FourierCirclesScene(Scene):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
# TODO, this should be a general animated mobect
|
# TODO, this should be a general animated mobect
|
||||||
|
def get_drawn_path_alpha(self):
|
||||||
|
return self.get_vector_time()
|
||||||
|
|
||||||
def get_drawn_path(self, vectors, stroke_width=2, **kwargs):
|
def get_drawn_path(self, vectors, stroke_width=2, **kwargs):
|
||||||
path = self.get_vector_sum_path(vectors, **kwargs)
|
path = self.get_vector_sum_path(vectors, **kwargs)
|
||||||
broken_path = CurvesAsSubmobjects(path)
|
broken_path = CurvesAsSubmobjects(path)
|
||||||
@ -162,7 +165,7 @@ class FourierCirclesScene(Scene):
|
|||||||
|
|
||||||
def update_path(path, dt):
|
def update_path(path, dt):
|
||||||
# alpha = path.curr_time * self.get_slow_factor()
|
# alpha = path.curr_time * self.get_slow_factor()
|
||||||
alpha = self.get_vector_time()
|
alpha = self.get_drawn_path_alpha()
|
||||||
n_curves = len(path)
|
n_curves = len(path)
|
||||||
for a, sp in zip(np.linspace(0, 1, n_curves), path):
|
for a, sp in zip(np.linspace(0, 1, n_curves), path):
|
||||||
b = alpha - a
|
b = alpha - a
|
||||||
|
@ -166,6 +166,9 @@ class BringTwoRodsTogether(Scene):
|
|||||||
},
|
},
|
||||||
"graph_x_min": 0,
|
"graph_x_min": 0,
|
||||||
"graph_x_max": 10,
|
"graph_x_max": 10,
|
||||||
|
"midpoint": 5,
|
||||||
|
"max_temp": 90,
|
||||||
|
"min_temp": 10,
|
||||||
"wait_time": 30,
|
"wait_time": 30,
|
||||||
"default_n_rod_pieces": 20,
|
"default_n_rod_pieces": 20,
|
||||||
"alpha": 1.0,
|
"alpha": 1.0,
|
||||||
@ -200,7 +203,7 @@ class BringTwoRodsTogether(Scene):
|
|||||||
x_min=self.graph_x_min,
|
x_min=self.graph_x_min,
|
||||||
x_max=self.graph_x_max,
|
x_max=self.graph_x_max,
|
||||||
step_size=self.step_size,
|
step_size=self.step_size,
|
||||||
discontinuities=[5],
|
discontinuities=[self.midpoint],
|
||||||
)
|
)
|
||||||
graph.color_using_background_image("VerticalTempGradient")
|
graph.color_using_background_image("VerticalTempGradient")
|
||||||
|
|
||||||
@ -315,20 +318,27 @@ class BringTwoRodsTogether(Scene):
|
|||||||
rods.add_updater(self.update_rods)
|
rods.add_updater(self.update_rods)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
ClockPassesTime(
|
self.get_clock_anim(self.wait_time),
|
||||||
self.clock,
|
|
||||||
run_time=self.wait_time,
|
|
||||||
hours_passed=self.wait_time,
|
|
||||||
),
|
|
||||||
FadeOut(labels)
|
FadeOut(labels)
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
def get_clock_anim(self, time, **kwargs):
|
||||||
|
config = {
|
||||||
|
"run_time": time,
|
||||||
|
"hours_passed": time,
|
||||||
|
}
|
||||||
|
config.update(kwargs)
|
||||||
|
return ClockPassesTime(self.clock, **kwargs)
|
||||||
|
|
||||||
def initial_function(self, x):
|
def initial_function(self, x):
|
||||||
if x <= 5:
|
epsilon = 1e-10
|
||||||
return 90
|
if x < self.midpoint - epsilon:
|
||||||
|
return self.max_temp
|
||||||
|
elif x > self.midpoint + epsilon:
|
||||||
|
return self.min_temp
|
||||||
else:
|
else:
|
||||||
return 10
|
return (self.min_temp + self.max_temp) / 2
|
||||||
|
|
||||||
def update_graph(self, graph, dt, alpha=None, n_mini_steps=500):
|
def update_graph(self, graph, dt, alpha=None, n_mini_steps=500):
|
||||||
if alpha is None:
|
if alpha is None:
|
||||||
@ -432,7 +442,10 @@ class BringTwoRodsTogether(Scene):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def y_to_color(self, y):
|
def y_to_color(self, y):
|
||||||
return temperature_to_color((y - 45) / 45)
|
y_max = self.max_temp
|
||||||
|
y_min = self.min_temp
|
||||||
|
alpha = inverse_interpolate(y_min, y_max, y)
|
||||||
|
return temperature_to_color(interpolate(-0.8, 0.8, alpha))
|
||||||
|
|
||||||
def rod_point_to_color(self, point):
|
def rod_point_to_color(self, point):
|
||||||
return self.y_to_color(
|
return self.y_to_color(
|
||||||
|
@ -248,9 +248,27 @@ class FourierSeriesIllustraiton(Scene):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def get_sine_graphs(self, axes):
|
||||||
n_range = self.n_range
|
sine_graphs = VGroup(*[
|
||||||
|
axes.get_graph(self.generate_nth_func(n))
|
||||||
|
for n in self.n_range
|
||||||
|
])
|
||||||
|
sine_graphs.set_stroke(width=3)
|
||||||
|
sine_graphs.set_color_by_gradient(
|
||||||
|
BLUE, GREEN, RED, YELLOW, PINK,
|
||||||
|
BLUE, GREEN, RED, YELLOW, PINK,
|
||||||
|
)
|
||||||
|
return sine_graphs
|
||||||
|
|
||||||
|
def get_partial_sums(self, axes, sine_graphs):
|
||||||
|
partial_sums = VGroup(*[
|
||||||
|
axes.get_graph(self.generate_kth_partial_sum_func(k + 1))
|
||||||
|
for k in range(len(self.n_range))
|
||||||
|
])
|
||||||
|
partial_sums.match_style(sine_graphs)
|
||||||
|
return partial_sums
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
axes1 = Axes(**self.axes_config)
|
axes1 = Axes(**self.axes_config)
|
||||||
axes1.x_axis.add_numbers(
|
axes1.x_axis.add_numbers(
|
||||||
0.5, 1,
|
0.5, 1,
|
||||||
@ -266,21 +284,8 @@ class FourierSeriesIllustraiton(Scene):
|
|||||||
group.shift(2 * UP)
|
group.shift(2 * UP)
|
||||||
group.shift_onto_screen()
|
group.shift_onto_screen()
|
||||||
|
|
||||||
sine_graphs = VGroup(*[
|
sine_graphs = self.get_sine_graphs(axes1)
|
||||||
axes1.get_graph(self.generate_nth_func(n))
|
partial_sums = self.get_partial_sums(axes1, sine_graphs)
|
||||||
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 = self.get_sum_tex()
|
||||||
sum_tex.next_to(axes1, DOWN, LARGE_BUFF)
|
sum_tex.next_to(axes1, DOWN, LARGE_BUFF)
|
||||||
|
@ -137,19 +137,19 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||||||
|
|
||||||
def get_surface(self, axes, func, **kwargs):
|
def get_surface(self, axes, func, **kwargs):
|
||||||
config = {
|
config = {
|
||||||
"u_min": axes.x_min,
|
"u_min": axes.y_min,
|
||||||
"u_max": axes.x_max,
|
"u_max": axes.y_max,
|
||||||
"v_min": axes.y_min,
|
"v_min": axes.x_min,
|
||||||
"v_max": axes.y_max,
|
"v_max": axes.x_max,
|
||||||
"resolution": (
|
"resolution": (
|
||||||
(axes.x_max - axes.x_min) // axes.x_axis.tick_frequency,
|
|
||||||
(axes.y_max - axes.y_min) // axes.y_axis.tick_frequency,
|
(axes.y_max - axes.y_min) // axes.y_axis.tick_frequency,
|
||||||
|
(axes.x_max - axes.x_min) // axes.x_axis.tick_frequency,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
config.update(self.default_surface_config)
|
config.update(self.default_surface_config)
|
||||||
config.update(kwargs)
|
config.update(kwargs)
|
||||||
return ParametricSurface(
|
return ParametricSurface(
|
||||||
lambda x, t: axes.c2p(
|
lambda t, x: axes.c2p(
|
||||||
x, t, func(x, t)
|
x, t, func(x, t)
|
||||||
),
|
),
|
||||||
**config
|
**config
|
||||||
@ -181,13 +181,12 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||||||
fill_color=WHITE,
|
fill_color=WHITE,
|
||||||
fill_opacity=0.2
|
fill_opacity=0.2
|
||||||
)
|
)
|
||||||
plane.add_updater(lambda m: m.move_to(
|
plane.add_updater(lambda m: m.shift(
|
||||||
axes.c2p(
|
axes.c2p(
|
||||||
axes.x_min,
|
axes.x_min,
|
||||||
t_tracker.get_value(),
|
t_tracker.get_value(),
|
||||||
axes.z_min,
|
axes.z_min,
|
||||||
),
|
) - plane.points[0]
|
||||||
IN + LEFT,
|
|
||||||
))
|
))
|
||||||
plane.t_tracker = t_tracker
|
plane.t_tracker = t_tracker
|
||||||
return plane
|
return plane
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from manimlib.imports import *
|
from manimlib.imports import *
|
||||||
|
|
||||||
from active_projects.ode.part2.fourier_series import FourierOfTrebleClef
|
from active_projects.diffyq.part2.fourier_series import FourierOfTrebleClef
|
||||||
|
|
||||||
|
|
||||||
class ComplexFourierSeriesExample(FourierOfTrebleClef):
|
class ComplexFourierSeriesExample(FourierOfTrebleClef):
|
||||||
@ -200,9 +200,9 @@ class FourierSeriesExampleWithRectForZoom(ComplexFourierSeriesExample):
|
|||||||
CONFIG = {
|
CONFIG = {
|
||||||
"n_vectors": 100,
|
"n_vectors": 100,
|
||||||
"slow_factor": 0.01,
|
"slow_factor": 0.01,
|
||||||
"rect_scale_factor": 0.15,
|
"rect_scale_factor": 0.1,
|
||||||
"parametric_function_step_size": 0.0001,
|
|
||||||
"start_drawn": True,
|
"start_drawn": True,
|
||||||
|
"drawing_height": 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
@ -211,17 +211,20 @@ class FourierSeriesExampleWithRectForZoom(ComplexFourierSeriesExample):
|
|||||||
rect = self.get_rect()
|
rect = self.get_rect()
|
||||||
rect.set_height(self.rect_scale_factor * FRAME_HEIGHT)
|
rect.set_height(self.rect_scale_factor * FRAME_HEIGHT)
|
||||||
rect.add_updater(lambda m: m.move_to(
|
rect.add_updater(lambda m: m.move_to(
|
||||||
center_of_mass([
|
self.get_rect_center()
|
||||||
v.get_end()
|
|
||||||
for v in self.vectors
|
|
||||||
])
|
|
||||||
))
|
))
|
||||||
self.add(rect)
|
self.add(rect)
|
||||||
self.run_one_cycle()
|
self.run_one_cycle()
|
||||||
|
|
||||||
|
def get_rect_center(self):
|
||||||
|
return center_of_mass([
|
||||||
|
v.get_end()
|
||||||
|
for v in self.vectors
|
||||||
|
])
|
||||||
|
|
||||||
def get_rect(self):
|
def get_rect(self):
|
||||||
return ScreenRectangle(
|
return ScreenRectangle(
|
||||||
color=WHITE,
|
color=BLUE,
|
||||||
stroke_width=2,
|
stroke_width=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -231,7 +234,8 @@ class ZoomedInFourierSeriesExample(FourierSeriesExampleWithRectForZoom, MovingCa
|
|||||||
"vector_config": {
|
"vector_config": {
|
||||||
"max_tip_length_to_length_ratio": 0.15,
|
"max_tip_length_to_length_ratio": 0.15,
|
||||||
"tip_length": 0.05,
|
"tip_length": 0.05,
|
||||||
}
|
},
|
||||||
|
"parametric_function_step_size": 0.001,
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
@ -240,3 +244,66 @@ class ZoomedInFourierSeriesExample(FourierSeriesExampleWithRectForZoom, MovingCa
|
|||||||
|
|
||||||
def get_rect(self):
|
def get_rect(self):
|
||||||
return self.camera_frame
|
return self.camera_frame
|
||||||
|
|
||||||
|
def add_vectors_circles_path(self):
|
||||||
|
super().add_vectors_circles_path()
|
||||||
|
for v in self.vectors:
|
||||||
|
if v.get_stroke_width() < 1:
|
||||||
|
v.set_stroke(width=1)
|
||||||
|
|
||||||
|
def get_path_end(self, vectors, stroke_width=2, **kwargs):
|
||||||
|
full_path = self.get_vector_sum_path(vectors, **kwargs)
|
||||||
|
path = VMobject()
|
||||||
|
path.set_stroke(YELLOW, stroke_width)
|
||||||
|
|
||||||
|
def update_path(p):
|
||||||
|
alpha = self.get_vector_time() % 1
|
||||||
|
p.pointwise_become_partial(
|
||||||
|
full_path, 0, np.clip(alpha, 0, 1),
|
||||||
|
)
|
||||||
|
p.points[-1] = vectors[-1].get_end()
|
||||||
|
|
||||||
|
path.add_updater(update_path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
def get_drawn_path_alpha(self):
|
||||||
|
return super().get_drawn_path_alpha() - 0.002
|
||||||
|
|
||||||
|
def get_drawn_path(self, vectors, stroke_width=2, **kwargs):
|
||||||
|
odp = super().get_drawn_path(vectors, stroke_width, **kwargs)
|
||||||
|
return VGroup(
|
||||||
|
odp,
|
||||||
|
self.get_path_end(vectors, stroke_width, **kwargs),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ZoomedInFourierSeriesExample10xMore(ZoomedInFourierSeriesExample):
|
||||||
|
CONFIG = {
|
||||||
|
"vector_config": {
|
||||||
|
"max_tip_length_to_length_ratio": 0.15 * 0.4,
|
||||||
|
"tip_length": 0.05 * 0.2,
|
||||||
|
"max_stroke_width_to_length_ratio": 80,
|
||||||
|
"stroke_width": 3,
|
||||||
|
},
|
||||||
|
"max_circle_stroke_width": 0.5,
|
||||||
|
"rect_scale_factor": 0.01,
|
||||||
|
# "parametric_function_step_size": 0.01,
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_rect_center(self):
|
||||||
|
return self.vectors[-1].get_end()
|
||||||
|
|
||||||
|
# def get_drawn_path(self, vectors, stroke_width=2, **kwargs):
|
||||||
|
# return self.get_path_end(vectors, stroke_width, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class FourierSeriesExampleWithRectForZoomTrebleClef(FourierSeriesExampleWithRectForZoom):
|
||||||
|
CONFIG = {
|
||||||
|
"file_name": "TrebleClef",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ZoomedInFourierSeriesExampleTrebleClef(ZoomedInFourierSeriesExample):
|
||||||
|
CONFIG = {
|
||||||
|
"file_name": "TrebleClef",
|
||||||
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
from manimlib.imports import *
|
from manimlib.imports import *
|
||||||
from active_projects.diffyq.part3.temperature_graphs import TemperatureGraphScene
|
|
||||||
from active_projects.diffyq.part2.wordy_scenes import WriteHeatEquationTemplate
|
|
||||||
|
|
||||||
|
|
||||||
class RelationToOtherVideos(Scene):
|
class RelationToOtherVideos(Scene):
|
||||||
@ -104,242 +102,82 @@ class RelationToOtherVideos(Scene):
|
|||||||
return thumbnails
|
return thumbnails
|
||||||
|
|
||||||
|
|
||||||
class ShowLinearity(WriteHeatEquationTemplate, TemperatureGraphScene):
|
class FourierGainsImmortality(Scene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"temp_text": "Temp",
|
"mathematicians": [
|
||||||
"alpha": 0.1,
|
"Pythagoras",
|
||||||
"axes_config": {
|
"Euclid",
|
||||||
"z_max": 2,
|
"Archimedes",
|
||||||
"z_min": -2,
|
"Fermat",
|
||||||
"z_axis_config": {
|
"Newton",
|
||||||
"tick_frequency": 0.5,
|
"Leibniz",
|
||||||
"unit_size": 1.5,
|
"Johann_Bernoulli2",
|
||||||
},
|
"Euler",
|
||||||
},
|
"Joseph Fourier",
|
||||||
"default_surface_config": {
|
"Gauss",
|
||||||
"resolution": (16, 16)
|
"Riemann",
|
||||||
# "resolution": (4, 4)
|
"Cantor",
|
||||||
},
|
"Noether",
|
||||||
"freqs": [2, 5],
|
"Ramanujan",
|
||||||
|
"Godel",
|
||||||
|
"Turing",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
TemperatureGraphScene.setup(self)
|
|
||||||
WriteHeatEquationTemplate.setup(self)
|
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.init_camera()
|
fourier = ImageMobject("Joseph Fourier")
|
||||||
self.add_three_graphs()
|
fourier.set_height(5)
|
||||||
self.show_words()
|
fourier.to_edge(LEFT)
|
||||||
self.add_function_labels()
|
name = TextMobject("Joseph Fourier")
|
||||||
self.change_scalars()
|
name.next_to(fourier, DOWN)
|
||||||
|
|
||||||
def init_camera(self):
|
immortals = self.get_immortals()
|
||||||
self.camera.set_distance(1000)
|
immortals.remove(immortals.fourier)
|
||||||
|
immortals.to_edge(RIGHT)
|
||||||
|
|
||||||
def add_three_graphs(self):
|
self.add(fourier, name)
|
||||||
axes_group = self.get_axes_group()
|
self.play(LaggedStartMap(
|
||||||
axes0, axes1, axes2 = axes_group
|
FadeIn, immortals,
|
||||||
freqs = self.freqs
|
lag_ratio=0.1,
|
||||||
scalar_trackers = Group(
|
run_time=2,
|
||||||
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(
|
self.play(
|
||||||
ShowCreation(arrow),
|
TransformFromCopy(fourier, immortals.fourier)
|
||||||
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.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
|
def get_immortals(self):
|
||||||
self.play(
|
images = Group(*[
|
||||||
FadeIn(T1[0]),
|
ImageMobject(name)
|
||||||
FadeIn(T2[0]),
|
for name in self.mathematicians
|
||||||
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)
|
for image in images:
|
||||||
axes_group.set_width(FRAME_WIDTH - 1)
|
image.set_height(1)
|
||||||
axes_group.to_edge(DOWN, buff=1)
|
images.arrange_in_grid(n_rows=4)
|
||||||
return axes_group
|
|
||||||
|
|
||||||
def get_axes(self):
|
last_row = images[-4:]
|
||||||
axes = self.get_three_d_axes()
|
low_center = last_row.get_center()
|
||||||
# axes.input_plane.set_fill(opacity=0)
|
last_row.arrange(RIGHT, buff=0.4, center=False)
|
||||||
# axes.input_plane.set_stroke(width=0.5)
|
last_row.move_to(low_center)
|
||||||
# axes.add(axes.input_plane)
|
|
||||||
self.orient_three_d_mobject(axes)
|
frame = SurroundingRectangle(images)
|
||||||
axes.rotate(-5 * DEGREES, UP)
|
frame.set_color(WHITE)
|
||||||
axes.set_width(4)
|
title = TextMobject("Immortals of Math")
|
||||||
axes.x_axis.label.next_to(
|
title.match_width(frame)
|
||||||
axes.x_axis.get_end(), DOWN,
|
title.next_to(frame, UP)
|
||||||
buff=2 * SMALL_BUFF
|
|
||||||
|
result = Group(title, frame, *images)
|
||||||
|
result.set_height(FRAME_HEIGHT - 1)
|
||||||
|
result.to_edge(RIGHT)
|
||||||
|
for image, name in zip(images, self.mathematicians):
|
||||||
|
setattr(
|
||||||
|
result,
|
||||||
|
name.split(" ")[-1].lower(),
|
||||||
|
image,
|
||||||
)
|
)
|
||||||
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
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class WhichWavesAreAvailable(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
||||||
|
241
active_projects/diffyq/part4/temperature_scenes.py
Normal file
241
active_projects/diffyq/part4/temperature_scenes.py
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
from manimlib.imports import *
|
||||||
|
from active_projects.diffyq.part2.heat_equation import BringTwoRodsTogether
|
||||||
|
from active_projects.diffyq.part3.staging import FourierSeriesIllustraiton
|
||||||
|
|
||||||
|
|
||||||
|
class StepFunctionExample(BringTwoRodsTogether, FourierSeriesIllustraiton):
|
||||||
|
CONFIG = {
|
||||||
|
"axes_config": {
|
||||||
|
"y_min": -1.5,
|
||||||
|
"y_max": 1.5,
|
||||||
|
"y_axis_config": {
|
||||||
|
"unit_size": 2.5,
|
||||||
|
"tick_frequency": 0.5,
|
||||||
|
},
|
||||||
|
"x_min": 0,
|
||||||
|
"x_max": 1,
|
||||||
|
"x_axis_config": {
|
||||||
|
"unit_size": 8,
|
||||||
|
"tick_frequency": 0.1,
|
||||||
|
"include_tip": False,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"graph_x_min": 0,
|
||||||
|
"graph_x_max": 1,
|
||||||
|
"midpoint": 0.5,
|
||||||
|
"min_temp": -1,
|
||||||
|
"max_temp": 1,
|
||||||
|
"alpha": 0.25,
|
||||||
|
"step_size": 0.01,
|
||||||
|
"n_range": range(1, 41, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.setup_axes()
|
||||||
|
self.setup_graph()
|
||||||
|
self.setup_clock()
|
||||||
|
|
||||||
|
self.bring_rods_together()
|
||||||
|
self.let_evolve_for_a_bit()
|
||||||
|
self.add_labels()
|
||||||
|
self.compare_to_sine_wave()
|
||||||
|
self.sum_of_sine_waves()
|
||||||
|
|
||||||
|
def bring_rods_together(self):
|
||||||
|
rods = VGroup(
|
||||||
|
self.get_rod(0, 0.5),
|
||||||
|
self.get_rod(0.5, 1),
|
||||||
|
)
|
||||||
|
rods.add_updater(self.update_rods)
|
||||||
|
|
||||||
|
arrows = VGroup(
|
||||||
|
Vector(RIGHT).next_to(rods[0], UP),
|
||||||
|
Vector(LEFT).next_to(rods[1], UP),
|
||||||
|
)
|
||||||
|
|
||||||
|
words = VGroup(
|
||||||
|
TextMobject("Hot").next_to(rods[0], DOWN),
|
||||||
|
TextMobject("Cold").next_to(rods[1], DOWN),
|
||||||
|
)
|
||||||
|
|
||||||
|
for pair in rods, words:
|
||||||
|
pair.save_state()
|
||||||
|
pair.space_out_submobjects(1.2)
|
||||||
|
|
||||||
|
black_rects = VGroup(*[
|
||||||
|
Square(
|
||||||
|
side_length=1,
|
||||||
|
fill_color=BLACK,
|
||||||
|
fill_opacity=1,
|
||||||
|
stroke_width=0,
|
||||||
|
).move_to(self.axes.c2p(0, u))
|
||||||
|
for u in [1, -1]
|
||||||
|
])
|
||||||
|
black_rects[0].add_updater(
|
||||||
|
lambda m: m.align_to(rods[0].get_right(), LEFT)
|
||||||
|
)
|
||||||
|
black_rects[1].add_updater(
|
||||||
|
lambda m: m.align_to(rods[1].get_left(), RIGHT)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(
|
||||||
|
self.axes,
|
||||||
|
self.graph,
|
||||||
|
self.clock,
|
||||||
|
)
|
||||||
|
self.add(rods, words)
|
||||||
|
self.add(black_rects)
|
||||||
|
|
||||||
|
kw = {
|
||||||
|
"run_time": 2,
|
||||||
|
"rate_func": rush_into,
|
||||||
|
}
|
||||||
|
self.play(
|
||||||
|
Restore(rods, **kw),
|
||||||
|
Restore(words, **kw),
|
||||||
|
*map(ShowCreation, arrows)
|
||||||
|
)
|
||||||
|
self.remove(black_rects)
|
||||||
|
|
||||||
|
self.to_fade = VGroup(words, arrows)
|
||||||
|
self.rods = rods
|
||||||
|
|
||||||
|
def let_evolve_for_a_bit(self):
|
||||||
|
rods = self.rods
|
||||||
|
# axes = self.axes
|
||||||
|
time_label = self.time_label
|
||||||
|
graph = self.graph
|
||||||
|
graph.save_state()
|
||||||
|
|
||||||
|
graph.add_updater(self.update_graph)
|
||||||
|
time_label.next_to(self.clock, DOWN)
|
||||||
|
time_label.add_updater(
|
||||||
|
lambda d, dt: d.increment_value(dt)
|
||||||
|
)
|
||||||
|
rods.add_updater(self.update_rods)
|
||||||
|
|
||||||
|
self.add(time_label)
|
||||||
|
self.play(
|
||||||
|
FadeOut(self.to_fade),
|
||||||
|
self.get_clock_anim(1)
|
||||||
|
)
|
||||||
|
self.play(self.get_clock_anim(3))
|
||||||
|
|
||||||
|
time_label.clear_updaters()
|
||||||
|
graph.clear_updaters()
|
||||||
|
self.play(
|
||||||
|
self.get_clock_anim(
|
||||||
|
-4,
|
||||||
|
run_time=1,
|
||||||
|
rate_func=smooth,
|
||||||
|
),
|
||||||
|
graph.restore,
|
||||||
|
time_label.set_value, 0,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def add_labels(self):
|
||||||
|
axes = self.axes
|
||||||
|
y_axis = axes.y_axis
|
||||||
|
x_axis = axes.x_axis
|
||||||
|
y_numbers = y_axis.get_number_mobjects(
|
||||||
|
*np.arange(-1, 1.5, 0.5),
|
||||||
|
number_config={
|
||||||
|
"unit": "^\\circ",
|
||||||
|
"num_decimal_places": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
x_numbers = x_axis.get_number_mobjects(
|
||||||
|
*np.arange(0.25, 1.25, 0.25),
|
||||||
|
number_config={
|
||||||
|
"num_decimal_places": 2,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(FadeIn(y_numbers))
|
||||||
|
self.play(ShowCreationThenFadeAround(y_numbers[-1]))
|
||||||
|
self.play(ShowCreationThenFadeAround(y_numbers[0]))
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
LaggedStartMap(
|
||||||
|
FadeInFrom, x_numbers,
|
||||||
|
lambda m: (m, UP)
|
||||||
|
),
|
||||||
|
self.rods.set_opacity, 0.8,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def compare_to_sine_wave(self):
|
||||||
|
phi_tracker = ValueTracker(0)
|
||||||
|
get_phi = phi_tracker.get_value
|
||||||
|
k_tracker = ValueTracker(TAU)
|
||||||
|
get_k = k_tracker.get_value
|
||||||
|
A_tracker = ValueTracker(1)
|
||||||
|
get_A = A_tracker.get_value
|
||||||
|
|
||||||
|
sine_wave = always_redraw(lambda: self.axes.get_graph(
|
||||||
|
lambda x: get_A() * np.sin(
|
||||||
|
get_k() * x - get_phi()
|
||||||
|
),
|
||||||
|
x_min=self.graph_x_min,
|
||||||
|
x_max=self.graph_x_max,
|
||||||
|
).color_using_background_image("VerticalTempGradient"))
|
||||||
|
|
||||||
|
self.play(ShowCreation(sine_wave, run_time=3))
|
||||||
|
self.wait()
|
||||||
|
self.play(A_tracker.set_value, 1.25)
|
||||||
|
self.play(A_tracker.set_value, 0.75)
|
||||||
|
self.play(
|
||||||
|
phi_tracker.set_value, -PI / 2,
|
||||||
|
k_tracker.set_value, 3 * TAU,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
k_tracker.set_value, PI,
|
||||||
|
A_tracker.set_value, 1,
|
||||||
|
run_time=3
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.sine_wave = sine_wave
|
||||||
|
|
||||||
|
def sum_of_sine_waves(self):
|
||||||
|
curr_sine_wave = self.sine_wave
|
||||||
|
axes = self.axes
|
||||||
|
|
||||||
|
sine_graphs = self.get_sine_graphs(axes)
|
||||||
|
partial_sums = self.get_partial_sums(axes, sine_graphs)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeOut(curr_sine_wave),
|
||||||
|
FadeIn(partial_sums[0])
|
||||||
|
)
|
||||||
|
# Copy-pasting from superclass...in theory,
|
||||||
|
# this should be better abstracted, but eh.
|
||||||
|
curr_partial_sum = partial_sums[0]
|
||||||
|
pairs = list(zip(sine_graphs, partial_sums))[1:]
|
||||||
|
for sine_graph, partial_sum in pairs:
|
||||||
|
anims1 = [
|
||||||
|
ShowCreation(sine_graph)
|
||||||
|
]
|
||||||
|
partial_sum.set_stroke(BLACK, 4, background=True)
|
||||||
|
anims2 = [
|
||||||
|
curr_partial_sum.set_stroke,
|
||||||
|
{"width": 1, "opacity": 0.25},
|
||||||
|
curr_partial_sum.set_stroke,
|
||||||
|
{"width": 0, "background": True},
|
||||||
|
ReplacementTransform(
|
||||||
|
sine_graph, partial_sum,
|
||||||
|
remover=True
|
||||||
|
),
|
||||||
|
]
|
||||||
|
self.play(*anims1)
|
||||||
|
self.play(*anims2)
|
||||||
|
curr_partial_sum = partial_sum
|
||||||
|
|
||||||
|
#
|
||||||
|
def setup_axes(self):
|
||||||
|
super().setup_axes()
|
||||||
|
self.axes.shift(
|
||||||
|
self.axes.c2p(0, 0)[1] * DOWN
|
||||||
|
)
|
474
active_projects/diffyq/part4/three_d_graphs.py
Normal file
474
active_projects/diffyq/part4/three_d_graphs.py
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
from manimlib.imports import *
|
||||||
|
from active_projects.diffyq.part3.temperature_graphs import TemperatureGraphScene
|
||||||
|
from active_projects.diffyq.part2.wordy_scenes import WriteHeatEquationTemplate
|
||||||
|
|
||||||
|
|
||||||
|
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, **kw)
|
||||||
|
self.play(s2.set_value, 0.3, **kw)
|
||||||
|
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, **kwargs):
|
||||||
|
axes = self.get_three_d_axes(**kwargs)
|
||||||
|
# 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.func = func
|
||||||
|
result.suspend_updating()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class CombineSeveralSolutions(ShowLinearity):
|
||||||
|
CONFIG = {
|
||||||
|
"default_surface_config": {
|
||||||
|
"resolution": (16, 16),
|
||||||
|
# "resolution": (4, 4),
|
||||||
|
},
|
||||||
|
"n_top_graphs": 5,
|
||||||
|
"axes_config": {
|
||||||
|
"y_max": 15,
|
||||||
|
},
|
||||||
|
"target_scalars": [
|
||||||
|
0.81, -0.53, 0.41, 0.62, -0.95
|
||||||
|
],
|
||||||
|
"final_run_time": 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.init_camera()
|
||||||
|
self.add_all_axes()
|
||||||
|
self.setup_all_graphs()
|
||||||
|
self.show_infinite_family()
|
||||||
|
self.show_sum()
|
||||||
|
self.show_time_passing()
|
||||||
|
|
||||||
|
def add_all_axes(self):
|
||||||
|
top_axes_group = VGroup(*[
|
||||||
|
self.get_axes(
|
||||||
|
z_min=-1.25,
|
||||||
|
z_max=1.25,
|
||||||
|
z_axis_config={
|
||||||
|
"unit_size": 2,
|
||||||
|
"tick_frequency": 0.5,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for x in range(self.n_top_graphs)
|
||||||
|
])
|
||||||
|
top_axes_group.arrange(RIGHT, buff=2)
|
||||||
|
top_axes_group.set_width(FRAME_WIDTH - 1.5)
|
||||||
|
top_axes_group.to_corner(UL)
|
||||||
|
dots = TexMobject("\\dots")
|
||||||
|
dots.next_to(top_axes_group, RIGHT)
|
||||||
|
|
||||||
|
low_axes = self.get_axes()
|
||||||
|
low_axes.center()
|
||||||
|
low_axes.scale(1.2)
|
||||||
|
low_axes.to_edge(DOWN, buff=SMALL_BUFF)
|
||||||
|
|
||||||
|
self.add(top_axes_group)
|
||||||
|
self.add(dots)
|
||||||
|
self.add(low_axes)
|
||||||
|
|
||||||
|
self.top_axes_group = top_axes_group
|
||||||
|
self.low_axes = low_axes
|
||||||
|
|
||||||
|
def setup_all_graphs(self):
|
||||||
|
scalar_trackers = Group(*[
|
||||||
|
ValueTracker(1)
|
||||||
|
for x in range(self.n_top_graphs)
|
||||||
|
])
|
||||||
|
freqs = np.arange(self.n_top_graphs)
|
||||||
|
freqs += 1
|
||||||
|
self.top_graphs = VGroup(*[
|
||||||
|
self.get_graph(axes, [n], [st])
|
||||||
|
for axes, n, st in zip(
|
||||||
|
self.top_axes_group,
|
||||||
|
freqs,
|
||||||
|
scalar_trackers,
|
||||||
|
)
|
||||||
|
])
|
||||||
|
self.low_graph = self.get_graph(
|
||||||
|
self.low_axes, freqs, scalar_trackers
|
||||||
|
)
|
||||||
|
|
||||||
|
self.scalar_trackers = scalar_trackers
|
||||||
|
|
||||||
|
def show_infinite_family(self):
|
||||||
|
top_axes_group = self.top_axes_group
|
||||||
|
top_graphs = self.top_graphs
|
||||||
|
scalar_trackers = self.scalar_trackers
|
||||||
|
|
||||||
|
decimals = self.get_decimals(
|
||||||
|
top_axes_group, scalar_trackers
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(LaggedStart(*[
|
||||||
|
AnimationGroup(
|
||||||
|
Write(graph[0]),
|
||||||
|
FadeIn(graph[1]),
|
||||||
|
)
|
||||||
|
for graph in top_graphs
|
||||||
|
]))
|
||||||
|
self.wait()
|
||||||
|
self.play(FadeIn(decimals))
|
||||||
|
for graph in top_graphs:
|
||||||
|
graph.resume_updating()
|
||||||
|
|
||||||
|
self.play(LaggedStart(*[
|
||||||
|
ApplyMethod(st.set_value, value)
|
||||||
|
for st, value in zip(
|
||||||
|
scalar_trackers,
|
||||||
|
self.target_scalars,
|
||||||
|
)
|
||||||
|
]), run_time=3)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def show_sum(self):
|
||||||
|
top_graphs = self.top_graphs
|
||||||
|
low_graph = self.low_graph
|
||||||
|
low_graph.resume_updating()
|
||||||
|
low_graph.update()
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
LaggedStart(*[
|
||||||
|
Transform(
|
||||||
|
top_graph.copy().set_fill(opacity=0),
|
||||||
|
low_graph.copy().set_fill(opacity=0),
|
||||||
|
remover=True,
|
||||||
|
)
|
||||||
|
for top_graph in top_graphs
|
||||||
|
]),
|
||||||
|
FadeIn(
|
||||||
|
low_graph,
|
||||||
|
rate_func=squish_rate_func(smooth, 0.7, 1)
|
||||||
|
),
|
||||||
|
run_time=3,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
def show_time_passing(self):
|
||||||
|
all_graphs = [*self.top_graphs, self.low_graph]
|
||||||
|
all_axes = [*self.top_axes_group, self.low_axes]
|
||||||
|
|
||||||
|
time_tracker = ValueTracker(0)
|
||||||
|
get_t = time_tracker.get_value
|
||||||
|
|
||||||
|
anims = [
|
||||||
|
ApplyMethod(
|
||||||
|
time_tracker.set_value, 1,
|
||||||
|
run_time=1,
|
||||||
|
rate_func=linear
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
for axes, graph_group in zip(all_axes, all_graphs):
|
||||||
|
graph_group.clear_updaters()
|
||||||
|
surface, gslice = graph_group
|
||||||
|
plane = self.get_const_time_plane(axes)
|
||||||
|
plane.t_tracker.add_updater(
|
||||||
|
lambda m: m.set_value(get_t())
|
||||||
|
)
|
||||||
|
gslice.axes = axes
|
||||||
|
gslice.func = graph_group.func
|
||||||
|
gslice.add_updater(lambda m: m.become(
|
||||||
|
self.get_time_slice_graph(
|
||||||
|
m.axes, m.func, t=get_t()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
self.add(gslice)
|
||||||
|
self.add(plane.t_tracker)
|
||||||
|
anims.append(FadeIn(plane))
|
||||||
|
|
||||||
|
self.play(*anims)
|
||||||
|
run_time = self.final_run_time
|
||||||
|
self.play(
|
||||||
|
time_tracker.increment_value, run_time,
|
||||||
|
run_time=run_time,
|
||||||
|
rate_func=linear,
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
def get_decimals(self, axes_group, scalar_trackers):
|
||||||
|
result = VGroup()
|
||||||
|
for axes, st in zip(axes_group, scalar_trackers):
|
||||||
|
decimal = DecimalNumber()
|
||||||
|
decimal.move_to(axes.get_bottom(), UP)
|
||||||
|
decimal.shift(SMALL_BUFF * RIGHT)
|
||||||
|
decimal.set_color(YELLOW)
|
||||||
|
decimal.scalar_tracker = st
|
||||||
|
times = TexMobject("\\times")
|
||||||
|
times.next_to(decimal, LEFT, SMALL_BUFF)
|
||||||
|
decimal.add_updater(lambda d: d.set_value(
|
||||||
|
d.scalar_tracker.get_value()
|
||||||
|
))
|
||||||
|
group = VGroup(times, decimal)
|
||||||
|
group.scale(0.7)
|
||||||
|
result.add(group)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class CycleThroughManyLinearCombinations(CombineSeveralSolutions):
|
||||||
|
CONFIG = {
|
||||||
|
"default_surface_config": {
|
||||||
|
"resolution": (16, 16),
|
||||||
|
# "resolution": (4, 4),
|
||||||
|
},
|
||||||
|
"n_cycles": 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.init_camera()
|
||||||
|
self.add_all_axes()
|
||||||
|
self.setup_all_graphs()
|
||||||
|
#
|
||||||
|
self.cycle_through_superpositions()
|
||||||
|
|
||||||
|
def cycle_through_superpositions(self):
|
||||||
|
top_graphs = self.top_graphs
|
||||||
|
low_graph = self.low_graph
|
||||||
|
scalar_trackers = self.scalar_trackers
|
||||||
|
self.add(self.get_decimals(
|
||||||
|
self.top_axes_group, scalar_trackers
|
||||||
|
))
|
||||||
|
|
||||||
|
for graph in [low_graph, *top_graphs]:
|
||||||
|
graph.resume_updating()
|
||||||
|
self.add(graph)
|
||||||
|
|
||||||
|
nst = len(scalar_trackers)
|
||||||
|
for x in range(self.n_cycles):
|
||||||
|
self.play(LaggedStart(*[
|
||||||
|
ApplyMethod(st.set_value, value)
|
||||||
|
for st, value in zip(
|
||||||
|
scalar_trackers,
|
||||||
|
3 * np.random.random(nst) - 1.5
|
||||||
|
)
|
||||||
|
]), run_time=3)
|
||||||
|
self.wait()
|
Reference in New Issue
Block a user