mirror of
https://github.com/3b1b/manim.git
synced 2025-09-19 04:41:56 +00:00
More animations for the heat equation
This commit is contained in:
parent
c1e2ee7843
commit
a1878308e0
5 changed files with 376 additions and 23 deletions
|
@ -1,6 +1,7 @@
|
||||||
from active_projects.ode.part2.staging import *
|
from active_projects.ode.part2.staging import *
|
||||||
from active_projects.ode.part2.fourier_series import *
|
from active_projects.ode.part2.fourier_series import *
|
||||||
from active_projects.ode.part2.heat_equation import *
|
from active_projects.ode.part2.heat_equation import *
|
||||||
|
from active_projects.ode.part2.pi_scenes import *
|
||||||
|
|
||||||
OUTPUT_DIRECTORY = "ode/part2"
|
OUTPUT_DIRECTORY = "ode/part2"
|
||||||
ALL_SCENE_CLASSES = [
|
ALL_SCENE_CLASSES = [
|
||||||
|
@ -28,4 +29,8 @@ ALL_SCENE_CLASSES = [
|
||||||
TwoDBodyWithManyTemperaturesGraph,
|
TwoDBodyWithManyTemperaturesGraph,
|
||||||
TwoDBodyWithManyTemperaturesContour,
|
TwoDBodyWithManyTemperaturesContour,
|
||||||
BringTwoRodsTogether,
|
BringTwoRodsTogether,
|
||||||
|
ShowEvolvingTempGraphWithArrows,
|
||||||
|
WriteHeatEquation,
|
||||||
|
ReactionsToInitialHeatEquation,
|
||||||
|
TalkThrough1DHeatGraph,
|
||||||
]
|
]
|
||||||
|
|
|
@ -387,7 +387,9 @@ class FourierNailAndGear(FourierOfTrebleClef):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"height": 6,
|
"height": 6,
|
||||||
"n_circles": 200,
|
"n_circles": 200,
|
||||||
"run_time": 10,
|
"run_time": 100,
|
||||||
|
"slow_factor": 0.01,
|
||||||
|
"parametric_function_step_size": 0.0001,
|
||||||
"arrow_config": {
|
"arrow_config": {
|
||||||
"tip_length": 0.1,
|
"tip_length": 0.1,
|
||||||
"stroke_width": 2,
|
"stroke_width": 2,
|
||||||
|
|
|
@ -153,7 +153,7 @@ class TwoDBodyWithManyTemperaturesContour(ExternallyAnimatedScene):
|
||||||
|
|
||||||
class BringTwoRodsTogether(Scene):
|
class BringTwoRodsTogether(Scene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"step_size": 0.1,
|
"step_size": 0.05,
|
||||||
"axes_config": {
|
"axes_config": {
|
||||||
"x_min": -1,
|
"x_min": -1,
|
||||||
"x_max": 11,
|
"x_max": 11,
|
||||||
|
@ -162,10 +162,12 @@ class BringTwoRodsTogether(Scene):
|
||||||
"y_axis_config": {
|
"y_axis_config": {
|
||||||
"unit_size": 0.06,
|
"unit_size": 0.06,
|
||||||
"tick_frequency": 10,
|
"tick_frequency": 10,
|
||||||
# "numbers_with_elongated_ticks": range(20, 100, 20)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"wait_time": 5,
|
"graph_x_min": 0,
|
||||||
|
"graph_x_max": 10,
|
||||||
|
"wait_time": 30,
|
||||||
|
"alpha": 1.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -173,9 +175,6 @@ class BringTwoRodsTogether(Scene):
|
||||||
self.setup_graph()
|
self.setup_graph()
|
||||||
self.setup_clock()
|
self.setup_clock()
|
||||||
|
|
||||||
self.add(self.axes)
|
|
||||||
self.add(self.graph)
|
|
||||||
|
|
||||||
self.show_rods()
|
self.show_rods()
|
||||||
self.show_equilibration()
|
self.show_equilibration()
|
||||||
|
|
||||||
|
@ -194,9 +193,9 @@ class BringTwoRodsTogether(Scene):
|
||||||
|
|
||||||
def setup_graph(self):
|
def setup_graph(self):
|
||||||
graph = self.axes.get_graph(
|
graph = self.axes.get_graph(
|
||||||
lambda x: 90 if x <= 5 else 10,
|
self.initial_function,
|
||||||
x_min=0,
|
x_min=self.graph_x_min,
|
||||||
x_max=10,
|
x_max=self.graph_x_max,
|
||||||
step_size=self.step_size,
|
step_size=self.step_size,
|
||||||
discontinuities=[5],
|
discontinuities=[5],
|
||||||
)
|
)
|
||||||
|
@ -229,34 +228,115 @@ class BringTwoRodsTogether(Scene):
|
||||||
self.clock = clock
|
self.clock = clock
|
||||||
|
|
||||||
def show_rods(self):
|
def show_rods(self):
|
||||||
pass
|
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):
|
def show_equilibration(self):
|
||||||
self.add(self.time_group)
|
rods = self.rods
|
||||||
self.add(self.clock)
|
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
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
self.graph.add_updater(self.update_graph)
|
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(
|
self.time_label.add_updater(
|
||||||
lambda d, dt: d.increment_value(dt)
|
lambda d, dt: d.increment_value(dt)
|
||||||
)
|
)
|
||||||
|
rods.add_updater(self.update_rods)
|
||||||
|
|
||||||
self.play(
|
self.play(
|
||||||
ClockPassesTime(
|
ClockPassesTime(
|
||||||
self.clock,
|
self.clock,
|
||||||
run_time=self.wait_time,
|
run_time=self.wait_time,
|
||||||
hours_passed=self.wait_time,
|
hours_passed=self.wait_time,
|
||||||
)
|
),
|
||||||
|
FadeOut(labels)
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
def update_graph(self, graph, dt, alpha=1.0, n_mini_steps=100):
|
def initial_function(self, x):
|
||||||
|
if x <= 5:
|
||||||
|
return 90
|
||||||
|
else:
|
||||||
|
return 10
|
||||||
|
|
||||||
|
def update_graph(self, graph, dt, alpha=None, n_mini_steps=100):
|
||||||
|
if alpha is None:
|
||||||
|
alpha = self.alpha
|
||||||
points = np.append(
|
points = np.append(
|
||||||
graph.get_start_anchors(),
|
graph.get_start_anchors(),
|
||||||
[graph.get_last_point()],
|
[graph.get_last_point()],
|
||||||
axis=0,
|
axis=0,
|
||||||
)
|
)
|
||||||
for k in range(n_mini_steps):
|
for k in range(n_mini_steps):
|
||||||
change = np.zeros(points.shape)
|
y_change = np.zeros(points.shape[0])
|
||||||
dx = points[1][0] - points[0][0]
|
dx = points[1][0] - points[0][0]
|
||||||
for i in range(len(points)):
|
for i in range(len(points)):
|
||||||
p = points[i]
|
p = points[i]
|
||||||
|
@ -270,12 +350,188 @@ class BringTwoRodsTogether(Scene):
|
||||||
second_deriv = 0.5 * d2y / dx
|
second_deriv = 0.5 * d2y / dx
|
||||||
second_deriv = 0
|
second_deriv = 0
|
||||||
|
|
||||||
change[i][1] = alpha * second_deriv * dt / n_mini_steps
|
y_change[i] = alpha * second_deriv * dt / n_mini_steps
|
||||||
|
|
||||||
change[0][1] = change[1][1]
|
# y_change[0] = y_change[1]
|
||||||
change[-1][1] = change[-2][1]
|
# y_change[-1] = y_change[-2]
|
||||||
# change[0][1] = 0
|
y_change[0] = 0
|
||||||
# change[-1][1] = 0
|
y_change[-1] = 0
|
||||||
points += change
|
y_change -= np.mean(y_change)
|
||||||
|
points[:, 1] += y_change
|
||||||
graph.set_points_smoothly(points)
|
graph.set_points_smoothly(points)
|
||||||
return graph
|
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)
|
||||||
|
]
|
||||||
|
d2y = ry - 2 * y + ly
|
||||||
|
return d2y / (dx**2)
|
||||||
|
|
||||||
|
def get_rod(self, x_min, x_max, n_pieces=20):
|
||||||
|
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_color(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,
|
||||||
|
)
|
||||||
|
y = axes.y_axis.p2n(
|
||||||
|
graph.point_from_proportion(alpha)
|
||||||
|
)
|
||||||
|
return temperature_to_color(
|
||||||
|
(y - 45) / 45
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
|
||||||
|
CONFIG = {
|
||||||
|
"alpha": 0.1,
|
||||||
|
"n_arrows": 20,
|
||||||
|
"wait_time": 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.add_axes()
|
||||||
|
self.add_graph()
|
||||||
|
self.add_clock()
|
||||||
|
self.add_rod()
|
||||||
|
self.add_arrows()
|
||||||
|
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)
|
||||||
|
|
||||||
|
def add_rod(self):
|
||||||
|
rod = self.rod = self.get_rod(0, 10)
|
||||||
|
self.add(rod)
|
||||||
|
|
||||||
|
def add_arrows(self):
|
||||||
|
graph = self.graph
|
||||||
|
x_min = self.graph_x_min
|
||||||
|
x_max = self.graph_x_max
|
||||||
|
|
||||||
|
xs = np.linspace(x_min, x_max, self.n_arrows + 2)[1:-1]
|
||||||
|
arrows = VGroup(*[Vector(DOWN) for x in xs])
|
||||||
|
|
||||||
|
def update_arrows(arrows):
|
||||||
|
for x, arrow in zip(xs, arrows):
|
||||||
|
d2y_dx2 = self.get_second_derivative(x)
|
||||||
|
mag = np.sign(d2y_dx2) * np.sqrt(abs(d2y_dx2))
|
||||||
|
mag = np.clip(mag, -2, 2)
|
||||||
|
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 let_play(self):
|
||||||
|
graph = self.graph
|
||||||
|
rod = self.rod
|
||||||
|
clock = self.clock
|
||||||
|
time_label = self.time_label
|
||||||
|
|
||||||
|
graph.add_updater(self.update_graph)
|
||||||
|
time_label.add_updater(
|
||||||
|
lambda d, dt: d.increment_value(dt)
|
||||||
|
)
|
||||||
|
rod.add_updater(self.color_rod_by_graph)
|
||||||
|
|
||||||
|
# return
|
||||||
|
self.play(
|
||||||
|
ClockPassesTime(
|
||||||
|
clock,
|
||||||
|
run_time=self.wait_time,
|
||||||
|
hours_passed=self.wait_time,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
def initial_function(self, x):
|
||||||
|
new_x = TAU * x / 10
|
||||||
|
return 50 + 20 * np.sum([
|
||||||
|
np.sin(new_x),
|
||||||
|
np.sin(2 * new_x),
|
||||||
|
0.5 * np.sin(3 * new_x),
|
||||||
|
0.3 * np.sin(4 * new_x),
|
||||||
|
0.3 * np.sin(5 * new_x),
|
||||||
|
0.2 * np.sin(7 * new_x),
|
||||||
|
0.1 * np.sin(21 * new_x),
|
||||||
|
0.05 * np.sin(41 * new_x),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
class TalkThrough1DHeatGraph(ShowEvolvingTempGraphWithArrows):
|
||||||
|
def construct(self):
|
||||||
|
self.add_axes()
|
||||||
|
self.add_graph()
|
||||||
|
self.add_rod()
|
||||||
|
|
||||||
|
#
|
||||||
|
def initial_function(self, x):
|
||||||
|
new_x = TAU * x / 10
|
||||||
|
return 50 + 20 * np.sum([
|
||||||
|
np.sin(new_x),
|
||||||
|
np.sin(2 * new_x),
|
||||||
|
0.5 * np.sin(3 * new_x),
|
||||||
|
0.3 * np.sin(4 * new_x),
|
||||||
|
0.3 * np.sin(5 * new_x),
|
||||||
|
0.2 * np.sin(7 * new_x),
|
||||||
|
])
|
||||||
|
|
23
active_projects/ode/part2/pi_scenes.py
Normal file
23
active_projects/ode/part2/pi_scenes.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
self.wait(3)
|
|
@ -86,3 +86,70 @@ class PartTwoOfTour(TourOfDifferentialEquations):
|
||||||
class CompareODEToPDE(Scene):
|
class CompareODEToPDE(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WriteHeatEquation(Scene):
|
||||||
|
def construct(self):
|
||||||
|
d1_words = TextMobject("Heat equation\\\\", "(1 dimension)")
|
||||||
|
d3_words = TextMobject("Heat equation\\\\", "(3 dimensions)")
|
||||||
|
|
||||||
|
kwargs = {
|
||||||
|
"tex_to_color_map": {
|
||||||
|
"{T}": YELLOW,
|
||||||
|
"{t}": WHITE,
|
||||||
|
"{x}": GREEN,
|
||||||
|
"{y}": RED,
|
||||||
|
"{z}": BLUE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d1_equation = TexMobject(
|
||||||
|
"{\\partial {T} \\over \\partial {t}}({x}, {t})="
|
||||||
|
"{\\partial^2 {T} \\over \\partial {x}^2} ({x}, {t})",
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
d3_equation = 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}",
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
d1_group = VGroup(d1_words, d1_equation)
|
||||||
|
d3_group = VGroup(d3_words, d3_equation)
|
||||||
|
groups = VGroup(d1_group, d3_group)
|
||||||
|
for group in groups:
|
||||||
|
group.arrange(DOWN, buff=MED_LARGE_BUFF)
|
||||||
|
groups.arrange(RIGHT, buff=2)
|
||||||
|
groups.to_edge(UP)
|
||||||
|
|
||||||
|
d3_rhs = d3_equation[6:]
|
||||||
|
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}", **kwargs)
|
||||||
|
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()
|
||||||
|
|
Loading…
Add table
Reference in a new issue