Files

304 lines
7.9 KiB
Python

from manimlib.imports import *
from active_projects.diffyq.part2.heat_equation import *
class ShowNewRuleAtDiscreteBoundary(DiscreteSetup):
CONFIG = {
"axes_config": {
"x_min": 0,
"stroke_width": 1,
"x_axis_config": {
"include_tip": False,
},
},
"freq_amplitude_pairs": [
(1, 0.5),
(2, 1),
(3, 0.5),
(4, 0.3),
],
"v_line_class": DashedLine,
"v_line_config": {
},
"step_size": 1,
"wait_time": 15,
"alpha": 0.25,
}
def construct(self):
self.add_axes()
self.set_points()
self.show_boundary_point_influenced_by_neighbor()
self.add_clock()
self.let_evolve()
def set_points(self):
axes = self.axes
for mob in axes.family_members_with_points():
if isinstance(mob, Line):
mob.set_stroke(width=1)
step_size = self.step_size
xs = np.arange(
axes.x_min,
axes.x_max + step_size,
step_size
)
dots = self.dots = self.get_dots(axes, xs)
self.v_lines = self.get_v_lines(dots)
self.rod_pieces = self.get_rod_pieces(dots)
# rod_pieces
self.add(self.dots)
self.add(self.v_lines)
self.add(self.rod_pieces)
def show_boundary_point_influenced_by_neighbor(self):
dots = self.dots
ld = dots[0]
ld_in = dots[1]
rd = dots[-1]
rd_in = dots[-2]
v_len = 0.75
l_arrow = Vector(v_len * LEFT)
l_arrow.move_to(ld.get_left(), RIGHT)
r_arrow = Vector(v_len * RIGHT)
r_arrow.move_to(rd.get_right(), LEFT)
arrows = VGroup(l_arrow, r_arrow)
q_marks = VGroup(*[
TexMobject("?").scale(1.5).next_to(
arrow, arrow.get_vector()
)
for arrow in arrows
])
arrows.set_color(YELLOW)
q_marks.set_color(YELLOW)
blocking_rects = VGroup(*[
BackgroundRectangle(VGroup(
*dots[i:-i],
*self.rod_pieces[i:-i]
))
for i in [1, 2]
])
for rect in blocking_rects:
rect.stretch(1.1, dim=1, about_edge=UP)
self.play(FadeIn(blocking_rects[0]))
self.play(
LaggedStartMap(ShowCreation, arrows),
LaggedStart(*[
FadeInFrom(q_mark, -arrow.get_vector())
for q_mark, arrow in zip(q_marks, arrows)
]),
run_time=1.5
)
self.wait()
# Point to inward neighbor
new_arrows = VGroup(*[
Arrow(
d1.get_center(),
VGroup(d1, d2).get_center(),
buff=0,
).match_style(l_arrow)
for d1, d2 in [(ld, ld_in), (rd, rd_in)]
])
new_arrows.match_style(arrows)
l_brace = Brace(VGroup(ld, ld_in), DOWN)
r_brace = Brace(VGroup(rd, rd_in), DOWN)
braces = VGroup(l_brace, r_brace)
for brace in braces:
brace.align_to(
self.axes.x_axis.get_center(), UP
)
brace.shift(SMALL_BUFF * DOWN)
brace.add(brace.get_tex("\\Delta x"))
self.play(
ReplacementTransform(arrows, new_arrows),
FadeOut(q_marks),
ReplacementTransform(*blocking_rects)
)
self.wait()
self.play(FadeInFrom(braces, UP))
self.wait()
self.play(
FadeOut(new_arrows),
FadeOut(blocking_rects[1]),
FadeOut(braces),
)
def add_clock(self):
super().add_clock()
self.time_label.add_updater(
lambda d, dt: d.increment_value(dt)
)
VGroup(
self.clock,
self.time_label
).shift(2 * LEFT)
def let_evolve(self):
dots = self.dots
dots.add_updater(self.update_dots)
wait_time = self.wait_time
self.play(
ClockPassesTime(
self.clock,
run_time=wait_time,
hours_passed=wait_time,
),
)
#
def get_dots(self, axes, xs):
dots = VGroup(*[
Dot(axes.c2p(x, self.temp_func(x, 0)))
for x in xs
])
max_width = 0.8 * self.step_size
for dot in dots:
dot.add_updater(self.update_dot_color)
if dot.get_width() > max_width:
dot.set_width(max_width)
return dots
def get_v_lines(self, dots):
return always_redraw(lambda: VGroup(*[
self.get_v_line(dot)
for dot in dots
]))
def get_v_line(self, dot):
x_axis = self.axes.x_axis
bottom = dot.get_bottom()
x = x_axis.p2n(bottom)
proj_point = x_axis.n2p(x)
return self.v_line_class(
proj_point, bottom,
**self.v_line_config,
)
def get_rod_pieces(self, dots):
axis = self.axes.x_axis
factor = 1 - np.exp(-(0.8 / self.step_size)**2)
width = factor * self.step_size
pieces = VGroup()
for dot in dots:
piece = Line(ORIGIN, width * RIGHT)
piece.set_stroke(width=5)
piece.move_to(dot)
piece.set_y(axis.get_center()[1])
piece.dot = dot
piece.add_updater(
lambda p: p.match_color(p.dot)
)
pieces.add(piece)
return pieces
def update_dot_color(self, dot):
y = self.axes.y_axis.p2n(dot.get_center())
dot.set_color(self.y_to_color(y))
def update_dots(self, dots, dt):
for ds in zip(dots, dots[1:], dots[2:]):
points = [d.get_center() for d in ds]
x0, x1, x2 = [p[0] for p in points]
dx = x1 - x0
y0, y1, y2 = [p[1] for p in points]
self.update_dot(
dot=ds[1],
dt=dt,
mean_diff=0.5 * (y2 - 2 * y1 + y0) / dx
)
if ds[0] is dots[0]:
self.update_dot(
dot=ds[0],
dt=dt,
mean_diff=(y1 - y0) / dx
)
elif ds[-1] is dots[-1]:
self.update_dot(
dot=ds[-1],
dt=dt,
mean_diff=(y1 - y2) / dx
)
def update_dot(self, dot, dt, mean_diff):
dot.shift(mean_diff * self.alpha * dt * UP)
class DiscreteEvolutionPoint25(ShowNewRuleAtDiscreteBoundary):
CONFIG = {
"step_size": 0.25,
"alpha": 0.5,
"wait_time": 30,
}
def construct(self):
self.add_axes()
self.set_points()
self.add_clock()
self.let_evolve()
class DiscreteEvolutionPoint1(DiscreteEvolutionPoint25):
CONFIG = {
"step_size": 0.1,
"v_line_config": {
"stroke_width": 1,
},
"wait_time": 30,
}
class FlatEdgesForDiscreteEvolution(DiscreteEvolutionPoint1):
CONFIG = {
"wait_time": 20,
"step_size": 0.1,
}
def let_evolve(self):
lines = VGroup(*[
Line(LEFT, RIGHT)
for x in range(2)
])
lines.set_width(1.5)
lines.set_stroke(WHITE, 5, opacity=0.5)
lines.add_updater(self.update_lines)
turn_animation_into_updater(
ShowCreation(lines, run_time=2)
)
self.add(lines)
super().let_evolve()
def update_lines(self, lines):
dots = self.dots
for line, dot in zip(lines, [dots[0], dots[-1]]):
line.move_to(dot)
class FlatEdgesForDiscreteEvolutionTinySteps(FlatEdgesForDiscreteEvolution):
CONFIG = {
"step_size": 0.025,
"wait_time": 10,
"v_line_class": Line,
"v_line_config": {
"stroke_opacity": 0.5,
}
}