mirror of
https://github.com/3b1b/manim.git
synced 2025-09-01 00:48:45 +00:00
New scenes for diffyq part 3
This commit is contained in:
parent
5dcb113996
commit
ab2318ff9d
5 changed files with 956 additions and 127 deletions
|
@ -1,6 +1,7 @@
|
|||
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 *
|
||||
|
||||
|
||||
OUTPUT_DIRECTORY = "ode/part3"
|
||||
|
@ -12,4 +13,11 @@ SCENES_IN_ORDER = [
|
|||
ThreeMainObservations,
|
||||
SimpleCosExpGraph,
|
||||
AddMultipleSolutions,
|
||||
IveHeardOfThis,
|
||||
FourierSeriesOfLineIllustration,
|
||||
BreakDownAFunction,
|
||||
ThreeConstraints,
|
||||
OceanOfPossibilities,
|
||||
InFouriersShoes,
|
||||
AnalyzeSineCurve,
|
||||
]
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.wordy_scenes import *
|
||||
|
||||
|
||||
class IveHeardOfThis(TeacherStudentsScene):
|
||||
|
@ -39,3 +40,61 @@ class IveHeardOfThis(TeacherStudentsScene):
|
|||
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):
|
||||
pass
|
||||
|
|
|
@ -423,88 +423,6 @@ class CircleAnimationOfF(FourierOfTrebleClef):
|
|||
return path
|
||||
|
||||
|
||||
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 ThreeMainObservations(Scene):
|
||||
def construct(self):
|
||||
fourier = ImageMobject("Joseph Fourier")
|
||||
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 waves",
|
||||
# "H",
|
||||
# "Heat equation",
|
||||
),
|
||||
TextMobject(
|
||||
"2)",
|
||||
# "Linearity"
|
||||
),
|
||||
TextMobject(
|
||||
"3)",
|
||||
# "Any$^{*}$ function is\\\\",
|
||||
# "a sum of sine waves",
|
||||
),
|
||||
)
|
||||
# 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=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)
|
||||
for observation in observations
|
||||
], lag_ratio=0.2))
|
||||
self.play(
|
||||
FadeOut(fourier),
|
||||
FadeOut(bubble),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class NewSceneName(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
|
|
@ -23,7 +23,12 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||
"stroke_color": WHITE,
|
||||
"background_image_file": "VerticalTempGradient",
|
||||
},
|
||||
"default_surface_style": {
|
||||
"default_surface_config": {
|
||||
"u_min": 0,
|
||||
"u_max": TAU,
|
||||
"v_min": 0,
|
||||
"v_max": 10,
|
||||
"resolution": (16, 10),
|
||||
"fill_opacity": 0.1,
|
||||
"checkerboard_colors": [LIGHT_GREY],
|
||||
"stroke_width": 0.5,
|
||||
|
@ -32,19 +37,68 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||
},
|
||||
}
|
||||
|
||||
def get_three_d_axes(self, include_labels=True, **kwargs):
|
||||
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)
|
||||
|
||||
# Add number labels
|
||||
# TODO?
|
||||
if include_numbers:
|
||||
self.add_axes_numbers(axes)
|
||||
|
||||
# Add axis labels
|
||||
if include_labels:
|
||||
self.add_axes_labels(axes)
|
||||
|
||||
# Adjust axis orinetations
|
||||
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", PI / 2),
|
||||
("\\pi", PI),
|
||||
("3 \\pi \\over 2", 3 * PI / 2),
|
||||
("\\tau", 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)
|
||||
|
||||
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_right(), DOWN)
|
||||
x_label.next_to(axes.x_axis.get_end(), RIGHT)
|
||||
axes.x_axis.label = x_label
|
||||
|
||||
t_label = TextMobject("Time")
|
||||
|
@ -58,40 +112,6 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||
axes.z_axis.label = temp_label
|
||||
for axis in axes:
|
||||
axis.add(axis.label)
|
||||
|
||||
# Adjust axis orinetations
|
||||
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
|
||||
surface_config = {
|
||||
"u_min": 0,
|
||||
"u_max": axes.x_max,
|
||||
"v_min": 0,
|
||||
"v_max": axes.y_max,
|
||||
"resolution": (16, 10),
|
||||
}
|
||||
axes.surface_config = surface_config
|
||||
input_plane = ParametricSurface(
|
||||
lambda x, t: axes.c2p(x, t, 0),
|
||||
# lambda x, t: np.array([x, t, 0]),
|
||||
**surface_config,
|
||||
)
|
||||
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 get_time_slice_graph(self, axes, func, t, **kwargs):
|
||||
|
@ -100,7 +120,7 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||
config.update(kwargs)
|
||||
return ParametricFunction(
|
||||
lambda x: axes.c2p(
|
||||
x, t, func(x)
|
||||
x, t, func(x, t)
|
||||
),
|
||||
t_min=axes.x_min,
|
||||
t_max=axes.x_max,
|
||||
|
@ -113,10 +133,10 @@ class TemperatureGraphScene(SpecialThreeDScene):
|
|||
)
|
||||
|
||||
def get_surface(self, axes, func, **kwargs):
|
||||
config = dict()
|
||||
config.update(axes.surface_config)
|
||||
config.update(self.default_surface_style)
|
||||
config.update(kwargs)
|
||||
config = merge_dicts_recursively(
|
||||
self.default_surface_config,
|
||||
kwargs
|
||||
)
|
||||
return ParametricSurface(
|
||||
lambda x, t: axes.c2p(
|
||||
x, t, func(x, t)
|
||||
|
@ -375,6 +395,7 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
discontinuities=self.get_initial_func_discontinuities(),
|
||||
color=YELLOW,
|
||||
)
|
||||
top_graph.set_stroke(width=4)
|
||||
|
||||
fourier_terms = self.get_fourier_cosine_terms(
|
||||
self.initial_func
|
||||
|
@ -436,7 +457,7 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
plusses = VGroup(*[
|
||||
TexMobject("+").next_to(
|
||||
axes.x_axis.get_end(),
|
||||
RIGHT, MED_LARGE_BUFF
|
||||
RIGHT, MED_SMALL_BUFF
|
||||
)
|
||||
for axes in low_axes_group
|
||||
])
|
||||
|
@ -469,16 +490,15 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
self.wait()
|
||||
self.play(
|
||||
LaggedStartMap(FadeIn, low_axes_group),
|
||||
FadeInFrom(low_words, UP),
|
||||
*[
|
||||
TransformFromCopy(top_graph, low_graph)
|
||||
for low_graph in low_graphs
|
||||
]
|
||||
)
|
||||
self.play(FadeInFrom(low_words, UP))
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStartMap(FadeInFromDown, plusses),
|
||||
Write(dots)
|
||||
LaggedStartMap(FadeInFromDown, [*plusses, dots]),
|
||||
)
|
||||
self.play(ShowCreation(arrow))
|
||||
self.wait()
|
||||
|
@ -506,7 +526,7 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
anims2.append(AnimationGroup(
|
||||
TransformFromCopy(graph, top_graph.copy()),
|
||||
Transform(
|
||||
surface.copy().fade(1),
|
||||
surface.copy().set_fill(opacity=0),
|
||||
top_surface,
|
||||
)
|
||||
))
|
||||
|
@ -530,9 +550,10 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
|
||||
self.play(LaggedStartMap(FadeInFromDown, low_checkmarks))
|
||||
self.wait()
|
||||
self.play(TransformFromCopy(
|
||||
low_checkmarks, VGroup(top_checkmark)
|
||||
))
|
||||
self.play(*[
|
||||
TransformFromCopy(low_checkmark, top_checkmark.copy())
|
||||
for low_checkmark in low_checkmarks
|
||||
])
|
||||
self.wait()
|
||||
|
||||
#
|
||||
|
@ -578,3 +599,673 @@ class BreakDownAFunction(SimpleCosExpGraph):
|
|||
]
|
||||
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.camera.frame_center.move_to(
|
||||
3 * RIGHT
|
||||
)
|
||||
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(1.25)
|
||||
axes.shift(1.5 * IN)
|
||||
|
||||
self.add(axes)
|
||||
self.axes = axes
|
||||
|
||||
def setup_surface(self):
|
||||
axes = self.axes
|
||||
k = self.k
|
||||
|
||||
# Parameters for surface function
|
||||
initial_As = [2] + [
|
||||
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,
|
||||
frame_center=ORIGIN,
|
||||
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,
|
||||
frame_center=3 * RIGHT,
|
||||
)
|
||||
|
||||
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.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, t: 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, t: -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)
|
||||
])
|
||||
|
|
153
active_projects/ode/part3/wordy_scenes.py
Normal file
153
active_projects/ode/part3/wordy_scenes.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.wordy_scenes import *
|
||||
|
||||
|
||||
class ThreeMainObservations(Scene):
|
||||
def construct(self):
|
||||
fourier = ImageMobject("Joseph Fourier")
|
||||
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 waves",
|
||||
# "H",
|
||||
# "Heat equation",
|
||||
),
|
||||
TextMobject(
|
||||
"2)",
|
||||
# "Linearity"
|
||||
),
|
||||
TextMobject(
|
||||
"3)",
|
||||
# "Any$^{*}$ function is\\\\",
|
||||
# "a sum of sine waves",
|
||||
),
|
||||
)
|
||||
# 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=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)
|
||||
for observation in observations
|
||||
], lag_ratio=0.2))
|
||||
self.play(
|
||||
FadeOut(fourier),
|
||||
FadeOut(bubble),
|
||||
)
|
||||
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)
|
Loading…
Add table
Reference in a new issue