Files

2982 lines
82 KiB
Python

from manimlib.imports import *
from active_projects.diffyq.part2.shared_constructs import *
class TwoDBodyWithManyTemperatures(ThreeDScene):
CONFIG = {
"cells_per_side": 20,
"body_height": 6,
}
def construct(self):
self.introduce_body()
self.show_temperature_at_all_points()
def introduce_body(self):
height = self.body_height
buff = 0.025
rows = VGroup(*[
VGroup(*[
Dot(
# stroke_width=0.5,
stroke_width=0,
fill_opacity=1,
)
for x in range(self.cells_per_side)
]).arrange(RIGHT, buff=buff)
for y in range(self.cells_per_side)
]).arrange(DOWN, buff=buff)
for row in rows[1::2]:
row.submobjects.reverse()
body = self.body = VGroup(*it.chain(*rows))
body.set_height(height)
body.center()
body.to_edge(LEFT)
axes = self.axes = Axes(
x_min=-5, x_max=5,
y_min=-5, y_max=5,
)
axes.match_height(body)
axes.move_to(body)
for cell in body:
self.color_cell(cell)
# body.set_stroke(WHITE, 0.5) # Do this?
plate = Square(
stroke_width=0,
fill_color=DARK_GREY,
sheen_direction=UL,
sheen_factor=1,
fill_opacity=1,
)
plate.replace(body)
plate_words = TextMobject("Piece of \\\\ metal")
plate_words.scale(2)
plate_words.set_stroke(BLACK, 2, background=True)
plate_words.set_color(BLACK)
plate_words.move_to(plate)
self.play(
DrawBorderThenFill(plate),
Write(
plate_words,
run_time=2,
rate_func=squish_rate_func(smooth, 0.5, 1)
)
)
self.wait()
self.remove(plate_words)
def show_temperature_at_all_points(self):
body = self.body
start_corner = body[0].get_center()
dot = Dot(radius=0.01, color=WHITE)
dot.move_to(start_corner)
get_point = dot.get_center
lhs = TexMobject("T = ")
lhs.next_to(body, RIGHT, LARGE_BUFF)
decimal = DecimalNumber(
num_decimal_places=1,
unit="^\\circ"
)
decimal.next_to(lhs, RIGHT, MED_SMALL_BUFF, DOWN)
decimal.add_updater(
lambda d: d.set_value(
40 + 50 * self.point_to_temp(get_point())
)
)
arrow = Arrow(color=YELLOW)
arrow.set_stroke(BLACK, 8, background=True)
arrow.tip.set_stroke(BLACK, 2, background=True)
# arrow.add_to_back(arrow.copy().set_stroke(BLACK, 5))
arrow.add_updater(lambda a: a.put_start_and_end_on(
lhs.get_left() + MED_SMALL_BUFF * LEFT,
get_point(),
))
dot.add_updater(lambda p: p.move_to(
body[-1] if (1 < len(body)) else start_corner
))
self.add(body, dot, lhs, decimal, arrow)
self.play(
ShowIncreasingSubsets(
body,
run_time=10,
rate_func=linear,
)
)
self.wait()
self.remove(dot)
self.play(
FadeOut(arrow),
FadeOut(lhs),
FadeOut(decimal),
)
#
def point_to_temp(self, point, time=0):
x, y = self.axes.point_to_coords(point)
return two_d_temp_func(
0.3 * x, 0.3 * y, t=time
)
def color_cell(self, cell, vect=RIGHT):
p0 = cell.get_corner(-vect)
p1 = cell.get_corner(vect)
colors = []
for point in p0, p1:
temp = self.point_to_temp(point)
color = temperature_to_color(temp)
colors.append(color)
cell.set_color(color=colors)
cell.set_sheen_direction(vect)
return cell
class TwoDBodyWithManyTemperaturesGraph(ExternallyAnimatedScene):
pass
class TwoDBodyWithManyTemperaturesContour(ExternallyAnimatedScene):
pass
class BringTwoRodsTogether(Scene):
CONFIG = {
"step_size": 0.05,
"axes_config": {
"x_min": -1,
"x_max": 11,
"y_min": -10,
"y_max": 100,
"y_axis_config": {
"unit_size": 0.06,
"tick_frequency": 10,
},
},
"y_labels": range(20, 100, 20),
"graph_x_min": 0,
"graph_x_max": 10,
"midpoint": 5,
"max_temp": 90,
"min_temp": 10,
"wait_time": 30,
"default_n_rod_pieces": 20,
"alpha": 1.0,
}
def construct(self):
self.setup_axes()
self.setup_graph()
self.setup_clock()
self.show_rods()
self.show_equilibration()
def setup_axes(self):
axes = Axes(**self.axes_config)
axes.center().to_edge(UP)
y_label = axes.get_y_axis_label("\\text{Temperature}")
y_label.to_edge(UP)
axes.y_axis.label = y_label
axes.y_axis.add(y_label)
axes.y_axis.add_numbers(*self.y_labels)
self.axes = axes
self.y_label = y_label
def setup_graph(self):
graph = self.axes.get_graph(
self.initial_function,
x_min=self.graph_x_min,
x_max=self.graph_x_max,
step_size=self.step_size,
discontinuities=[self.midpoint],
)
graph.color_using_background_image("VerticalTempGradient")
self.graph = graph
def setup_clock(self):
clock = Clock()
clock.set_height(1)
clock.to_corner(UR)
clock.shift(MED_LARGE_BUFF * LEFT)
time_lhs = TextMobject("Time: ")
time_label = DecimalNumber(
0, num_decimal_places=2,
)
time_rhs = TextMobject("s")
time_group = VGroup(
time_lhs,
time_label,
# time_rhs
)
time_group.arrange(RIGHT, aligned_edge=DOWN)
time_rhs.shift(SMALL_BUFF * LEFT)
time_group.next_to(clock, DOWN)
self.time_group = time_group
self.time_label = time_label
self.clock = clock
def show_rods(self):
rod1, rod2 = rods = VGroup(
self.get_rod(0, 5),
self.get_rod(5, 10),
)
rod1.set_color(rod1[0].get_color())
rod2.set_color(rod2[-1].get_color())
rods.save_state()
rods.space_out_submobjects(1.5)
rods.center()
labels = VGroup(
TexMobject("90^\\circ"),
TexMobject("10^\\circ"),
)
for rod, label in zip(rods, labels):
label.next_to(rod, DOWN)
rod.label = label
self.play(
FadeInFrom(rod1, UP),
Write(rod1.label),
)
self.play(
FadeInFrom(rod2, DOWN),
Write(rod2.label)
)
self.wait()
self.rods = rods
self.rod_labels = labels
def show_equilibration(self):
rods = self.rods
axes = self.axes
graph = self.graph
labels = self.rod_labels
self.play(
Write(axes),
rods.restore,
rods.space_out_submobjects, 1.1,
FadeIn(self.time_group),
FadeIn(self.clock),
*[
MaintainPositionRelativeTo(
rod.label, rod
)
for rod in rods
],
)
br1 = Rectangle(height=0.2, width=1)
br1.set_stroke(width=0)
br1.set_fill(BLACK, opacity=1)
br2 = br1.copy()
br1.add_updater(lambda b: b.move_to(axes.c2p(0, 90)))
br1.add_updater(
lambda b: b.align_to(rods[0].get_right(), LEFT)
)
br2.add_updater(lambda b: b.move_to(axes.c2p(0, 10)))
br2.add_updater(
lambda b: b.align_to(rods[1].get_left(), RIGHT)
)
self.add(graph, br1, br2)
self.play(
ShowCreation(graph),
labels[0].align_to, axes.c2p(0, 87), UP,
labels[1].align_to, axes.c2p(0, 13), DOWN,
)
self.play()
self.play(
rods.restore,
rate_func=rush_into,
)
self.remove(br1, br2)
graph.add_updater(self.update_graph)
self.time_label.add_updater(
lambda d, dt: d.increment_value(dt)
)
rods.add_updater(self.update_rods)
self.play(
self.get_clock_anim(self.wait_time),
FadeOut(labels)
)
#
def get_clock_anim(self, time, **kwargs):
config = {
"run_time": time,
"hours_passed": time,
}
config.update(kwargs)
return ClockPassesTime(self.clock, **config)
def initial_function(self, x):
epsilon = 1e-10
if x < self.midpoint - epsilon:
return self.max_temp
elif x > self.midpoint + epsilon:
return self.min_temp
else:
return (self.min_temp + self.max_temp) / 2
def update_graph(self, graph, dt, alpha=None, n_mini_steps=500):
if alpha is None:
alpha = self.alpha
points = np.append(
graph.get_start_anchors(),
[graph.get_last_point()],
axis=0,
)
for k in range(n_mini_steps):
y_change = np.zeros(points.shape[0])
dx = points[1][0] - points[0][0]
for i in range(len(points)):
p = points[i]
lp = points[max(i - 1, 0)]
rp = points[min(i + 1, len(points) - 1)]
d2y = (rp[1] - 2 * p[1] + lp[1])
if (0 < i < len(points) - 1):
second_deriv = d2y / (dx**2)
else:
second_deriv = 2 * d2y / (dx**2)
# second_deriv = 0
y_change[i] = alpha * second_deriv * dt / n_mini_steps
# y_change[0] = y_change[1]
# y_change[-1] = y_change[-2]
# y_change[0] = 0
# y_change[-1] = 0
# y_change -= np.mean(y_change)
points[:, 1] += y_change
graph.set_points_smoothly(points)
return graph
def get_second_derivative(self, x, dx=0.001):
graph = self.graph
x_min = self.graph_x_min
x_max = self.graph_x_max
ly, y, ry = [
graph.point_from_proportion(
inverse_interpolate(x_min, x_max, alt_x)
)[1]
for alt_x in (x - dx, x, x + dx)
]
# At the boundary, don't return the second deriv,
# but instead something matching the Neumann
# boundary condition.
if x == x_max:
return (ly - y) / dx
elif x == x_min:
return (ry - y) / dx
else:
d2y = ry - 2 * y + ly
return d2y / (dx**2)
def get_rod(self, x_min, x_max, n_pieces=None):
if n_pieces is None:
n_pieces = self.default_n_rod_pieces
axes = self.axes
line = Line(axes.c2p(x_min, 0), axes.c2p(x_max, 0))
rod = VGroup(*[
Square()
for n in range(n_pieces)
])
rod.arrange(RIGHT, buff=0)
rod.match_width(line)
rod.set_height(0.2, stretch=True)
rod.move_to(axes.c2p(x_min, 0), LEFT)
rod.set_fill(opacity=1)
rod.set_stroke(width=1)
rod.set_sheen_direction(RIGHT)
self.color_rod_by_graph(rod)
return rod
def update_rods(self, rods):
for rod in rods:
self.color_rod_by_graph(rod)
def color_rod_by_graph(self, rod):
for piece in rod:
piece.set_color(color=[
self.rod_point_to_color(piece.get_left()),
self.rod_point_to_color(piece.get_right()),
])
def rod_point_to_graph_y(self, point):
axes = self.axes
x = axes.x_axis.p2n(point)
graph = self.graph
alpha = inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
x,
)
return axes.y_axis.p2n(
graph.point_from_proportion(alpha)
)
def y_to_color(self, y):
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):
return self.y_to_color(
self.rod_point_to_graph_y(point)
)
class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
CONFIG = {
"alpha": 0.1,
"arrow_xs": np.linspace(0, 10, 22)[1:-1],
"arrow_scale_factor": 0.5,
"max_magnitude": 1.5,
"wait_time": 30,
"freq_amplitude_pairs": [
(1, 0.5),
(2, 1),
(3, 0.5),
(4, 0.3),
(5, 0.3),
(7, 0.2),
(21, 0.1),
(41, 0.05),
],
}
def construct(self):
self.add_axes()
self.add_graph()
self.add_clock()
self.add_rod()
self.add_arrows()
self.initialize_updaters()
self.let_play()
def add_axes(self):
self.setup_axes()
self.add(self.axes)
def add_graph(self):
self.setup_graph()
self.add(self.graph)
def add_clock(self):
self.setup_clock()
self.add(self.clock)
self.add(self.time_label)
self.time_label.next_to(self.clock, DOWN)
def add_rod(self):
rod = self.rod = self.get_rod(
self.graph_x_min,
self.graph_x_max,
)
self.add(rod)
def add_arrows(self):
graph = self.graph
x_min = self.graph_x_min
x_max = self.graph_x_max
xs = self.arrow_xs
arrows = VGroup(*[Vector(DOWN) for x in xs])
asf = self.arrow_scale_factor
def update_arrows(arrows):
for x, arrow in zip(xs, arrows):
d2y_dx2 = self.get_second_derivative(x)
mag = asf * np.sign(d2y_dx2) * abs(d2y_dx2)
mag = np.clip(
mag,
-self.max_magnitude,
self.max_magnitude,
)
arrow.put_start_and_end_on(
ORIGIN, mag * UP
)
point = graph.point_from_proportion(
inverse_interpolate(x_min, x_max, x)
)
arrow.shift(point - arrow.get_start())
arrow.set_color(
self.rod_point_to_color(point)
)
arrows.add_updater(update_arrows)
self.add(arrows)
self.arrows = arrows
def initialize_updaters(self):
if hasattr(self, "graph"):
self.graph.add_updater(self.update_graph)
if hasattr(self, "rod"):
self.rod.add_updater(self.color_rod_by_graph)
if hasattr(self, "time_label"):
self.time_label.add_updater(
lambda d, dt: d.increment_value(dt)
)
def let_play(self):
self.run_clock(self.wait_time)
def run_clock(self, time):
self.play(
ClockPassesTime(
self.clock,
run_time=time,
hours_passed=time,
),
)
#
def temp_func(self, x, t):
new_x = TAU * x / 10
return 50 + 20 * np.sum([
amp * np.sin(freq * new_x) *
np.exp(-(self.alpha * freq**2) * t)
for freq, amp in self.freq_amplitude_pairs
])
def initial_function(self, x, time=0):
return self.temp_func(x, 0)
class TalkThrough1DHeatGraph(ShowEvolvingTempGraphWithArrows, SpecialThreeDScene):
CONFIG = {
"freq_amplitude_pairs": [
(1, 0.5),
(2, 1),
(3, 0.5),
(4, 0.3),
(5, 0.3),
(7, 0.2),
],
"surface_resolution": 20,
"graph_slice_step": 10 / 20,
}
def construct(self):
self.add_axes()
self.add_graph()
self.add_rod()
self.emphasize_graph()
self.emphasize_rod()
self.show_x_axis()
self.show_changes_over_time()
self.show_surface()
def add_graph(self):
self.graph = self.get_graph()
self.add(self.graph)
def emphasize_graph(self):
graph = self.graph
q_marks = VGroup(*[
TexMobject("?").move_to(
graph.point_from_proportion(a),
UP,
).set_stroke(BLACK, 3, background=True)
for a in np.linspace(0, 1, 20)
])
self.play(LaggedStart(*[
Succession(
FadeInFromLarge(q_mark),
FadeOutAndShift(q_mark, DOWN),
)
for q_mark in q_marks
]))
self.wait()
def emphasize_rod(self):
alt_rod = self.get_rod(0, 10, 50)
word = TextMobject("Rod")
word.scale(2)
word.next_to(alt_rod, UP, MED_SMALL_BUFF)
self.play(
LaggedStart(
*[
Rotating(piece, rate_func=smooth)
for piece in alt_rod
],
run_time=2,
lag_ratio=0.01,
),
Write(word)
)
self.remove(*alt_rod)
self.wait()
self.rod_word = word
def show_x_axis(self):
rod = self.rod
axes = self.axes
graph = self.graph
x_axis = axes.x_axis
x_numbers = x_axis.get_number_mobjects(*range(1, 11))
x_axis_label = TexMobject("x")
x_axis_label.next_to(x_axis.get_right(), UP)
self.play(
rod.set_opacity, 0.5,
FadeInFrom(x_axis_label, UL),
LaggedStartMap(
FadeInFrom, x_numbers,
lambda m: (m, UP),
)
)
self.wait()
# Show x-values
triangle = ArrowTip(
start_angle=-90 * DEGREES,
color=LIGHT_GREY,
)
x_tracker = ValueTracker(PI)
get_x = x_tracker.get_value
def get_x_point():
return x_axis.n2p(get_x())
def get_graph_point():
return graph.point_from_proportion(
inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
get_x(),
)
)
triangle.add_updater(
lambda t: t.next_to(get_x_point(), UP)
)
x_label = VGroup(
TexMobject("x"),
TexMobject("="),
DecimalNumber(
0,
num_decimal_places=3,
include_background_rectangle=True,
).scale(0.9)
)
x_label.set_stroke(BLACK, 5, background=True)
x_label.add_updater(lambda m: m[-1].set_value(get_x()))
x_label.add_updater(lambda m: m.arrange(RIGHT, buff=SMALL_BUFF))
x_label.add_updater(lambda m: m[-1].align_to(m[0], DOWN))
x_label.add_updater(lambda m: m.next_to(triangle, UP, SMALL_BUFF))
x_label.add_updater(lambda m: m.shift(SMALL_BUFF * RIGHT))
rod_piece = always_redraw(
lambda: self.get_rod(
get_x() - 0.05, get_x() + 0.05,
n_pieces=1,
)
)
self.play(
FadeInFrom(triangle, UP),
FadeIn(x_label),
FadeIn(rod_piece),
FadeOut(self.rod_word),
)
for value in [np.exp(2), (np.sqrt(5) + 1) / 2]:
self.play(x_tracker.set_value, value, run_time=2)
self.wait()
# Show graph
v_line = always_redraw(
lambda: DashedLine(
get_x_point(),
get_graph_point(),
color=self.rod_point_to_color(get_x_point()),
)
)
graph_dot = Dot()
graph_dot.add_updater(
lambda m: m.set_color(
self.rod_point_to_color(m.get_center())
)
)
graph_dot.add_updater(
lambda m: m.move_to(get_graph_point())
)
t_label = TexMobject("T(", "x", ")")
t_label.set_stroke(BLACK, 3, background=True)
t_label.add_updater(
lambda m: m.next_to(graph_dot, UR, buff=0)
)
self.add(v_line, rod_piece, x_label, triangle)
self.play(
TransformFromCopy(x_label[0], t_label[1]),
FadeIn(t_label[0::2]),
ShowCreation(v_line),
GrowFromPoint(graph_dot, get_x_point()),
)
self.add(t_label)
self.wait()
self.play(
x_tracker.set_value, TAU,
run_time=5,
)
self.x_tracker = x_tracker
self.graph_label_group = VGroup(
v_line, rod_piece, triangle, x_label,
graph_dot, t_label,
)
self.set_variables_as_attrs(*self.graph_label_group)
self.set_variables_as_attrs(x_numbers, x_axis_label)
def show_changes_over_time(self):
graph = self.graph
t_label = self.t_label
new_t_label = TexMobject("T(", "x", ",", "t", ")")
new_t_label.set_color_by_tex("t", YELLOW)
new_t_label.match_updaters(t_label)
self.setup_clock()
clock = self.clock
time_label = self.time_label
time_group = self.time_group
time = 5
self.play(
FadeIn(clock),
FadeIn(time_group),
)
self.play(
self.get_graph_time_change_animation(
graph, time
),
ClockPassesTime(clock),
ChangeDecimalToValue(
time_label, time,
rate_func=linear,
),
ReplacementTransform(
t_label,
new_t_label,
rate_func=squish_rate_func(smooth, 0.5, 0.7),
),
run_time=time
)
self.play(
ShowCreationThenFadeAround(
new_t_label.get_part_by_tex("t")
),
)
self.wait()
self.play(
FadeOut(clock),
ChangeDecimalToValue(time_label, 0),
VFadeOut(time_group),
self.get_graph_time_change_animation(
graph,
new_time=0,
),
run_time=1,
rate_func=smooth,
)
self.graph_label_group.remove(t_label)
self.graph_label_group.add(new_t_label)
def show_surface(self):
axes = self.axes
graph = self.graph
t_min = 0
t_max = 10
axes_copy = axes.deepcopy()
self.original_axes = self.axes
# Set rod final state
final_graph = self.get_graph(t_max)
curr_graph = self.graph
self.graph = final_graph
final_rod = self.rod.copy()
final_rod.set_opacity(1)
self.color_rod_by_graph(final_rod)
self.graph = curr_graph
# Time axis
t_axis = NumberLine(
x_min=t_min,
x_max=t_max,
)
origin = axes.c2p(0, 0)
t_axis.shift(origin - t_axis.n2p(0))
t_axis.add_numbers(
*range(1, t_max + 1),
direction=UP,
)
time_label = TextMobject("Time")
time_label.scale(1.5)
time_label.next_to(t_axis, UP)
t_axis.time_label = time_label
t_axis.add(time_label)
# t_axis.rotate(90 * DEGREES, LEFT, about_point=origin)
t_axis.rotate(90 * DEGREES, UP, about_point=origin)
# New parts of graph
step = self.graph_slice_step
graph_slices = VGroup(*[
self.get_graph(time=t).shift(
t * IN
)
for t in np.arange(0, t_max + step, step)
])
graph_slices.set_stroke(width=1)
graph_slices.set_shade_in_3d(True)
# Input plane
x_axis = self.axes.x_axis
y = axes.c2p(0, 0)[1]
surface_config = {
"u_min": self.graph_x_min,
"u_max": self.graph_x_max,
"v_min": t_min,
"v_max": t_max,
"resolution": self.surface_resolution,
}
input_plane = ParametricSurface(
lambda x, t: np.array([
x_axis.n2p(x)[0],
y,
t_axis.n2p(t)[2],
]),
**surface_config,
)
input_plane.set_style(
fill_opacity=0.5,
fill_color=BLUE_B,
stroke_width=0.5,
stroke_color=WHITE,
)
# Surface
y_axis = axes.y_axis
surface = ParametricSurface(
lambda x, t: np.array([
x_axis.n2p(x)[0],
y_axis.n2p(self.temp_func(x, t))[1],
t_axis.n2p(t)[2],
]),
**surface_config,
)
surface.set_style(
fill_opacity=0.1,
fill_color=LIGHT_GREY,
stroke_width=0.5,
stroke_color=WHITE,
stroke_opacity=0.5,
)
# Rotate everything on screen and move camera
# in such a way that it looks the same
curr_group = Group(*self.get_mobjects())
curr_group.clear_updaters()
self.set_camera_orientation(
phi=90 * DEGREES,
)
mobs = [
curr_group,
graph_slices,
t_axis,
input_plane,
surface,
]
for mob in mobs:
self.orient_mobject_for_3d(mob)
# Clean current mobjects
self.x_label.set_stroke(BLACK, 2, background=True)
self.x_label[-1][0].fade(1)
self.move_camera(
phi=80 * DEGREES,
theta=-85 * DEGREES,
added_anims=[
Write(input_plane),
Write(t_axis),
FadeOut(self.graph_label_group),
self.rod.set_opacity, 1,
]
)
self.begin_ambient_camera_rotation()
self.add(*graph_slices, *self.get_mobjects())
self.play(
FadeIn(surface),
LaggedStart(*[
TransformFromCopy(graph, graph_slice)
for graph_slice in graph_slices
], lag_ratio=0.02)
)
self.wait(4)
# Show slices
self.axes = axes_copy # So get_graph works...
slicing_plane = Rectangle(
stroke_width=0,
fill_color=WHITE,
fill_opacity=0.2,
)
slicing_plane.set_shade_in_3d(True)
slicing_plane.replace(
Line(axes_copy.c2p(0, 0), axes_copy.c2p(10, 100)),
stretch=True
)
self.orient_mobject_for_3d(slicing_plane)
def get_time_slice(t):
new_slice = self.get_graph(t)
new_slice.set_shade_in_3d(True)
self.orient_mobject_for_3d(new_slice)
new_slice.shift(t * UP)
return new_slice
graph.set_shade_in_3d(True)
t_tracker = ValueTracker(0)
graph.add_updater(lambda g: g.become(
get_time_slice(t_tracker.get_value())
))
self.orient_mobject_for_3d(final_rod)
final_rod.shift(10 * UP)
kw = {"run_time": 10, "rate_func": linear}
self.rod.save_state()
self.play(
ApplyMethod(t_tracker.set_value, 10, **kw),
Transform(self.rod, final_rod, **kw),
ApplyMethod(slicing_plane.shift, 10 * UP, **kw),
)
self.wait()
self.set_variables_as_attrs(
t_axis,
input_plane,
surface,
graph_slices,
slicing_plane,
t_tracker,
)
#
def get_graph(self, time=0):
graph = self.axes.get_graph(
lambda x: self.temp_func(x, time),
x_min=self.graph_x_min,
x_max=self.graph_x_max,
step_size=self.step_size,
)
graph.time = time
graph.color_using_background_image("VerticalTempGradient")
return graph
def get_graph_time_change_animation(self, graph, new_time, **kwargs):
old_time = graph.time
graph.time = new_time
config = {
"run_time": abs(new_time - old_time),
"rate_func": linear,
}
config.update(kwargs)
return UpdateFromAlphaFunc(
graph,
lambda g, a: g.become(
self.get_graph(interpolate(
old_time, new_time, a
))
),
**config
)
def orient_mobject_for_3d(self, mob):
mob.rotate(
90 * DEGREES,
axis=RIGHT,
about_point=ORIGIN
)
return mob
class ContrastXChangesToTChanges(TalkThrough1DHeatGraph):
CONFIG = {
# "surface_resolution": 5,
# "graph_slice_step": 1,
}
def construct(self):
self.catchup_with_last_scene()
self.emphasize_dimensions_of_input_space()
self.reset_time_to_zero()
self.show_changes_with_x()
self.show_changes_with_t()
def catchup_with_last_scene(self):
self.force_skipping()
self.add_axes()
self.add_graph()
self.add_rod()
self.rod_word = Point()
self.show_x_axis()
self.show_surface()
self.revert_to_original_skipping_status()
def emphasize_dimensions_of_input_space(self):
plane = self.input_plane
plane_copy = plane.copy()
plane_copy.set_color(BLUE_E)
plane_copy.shift(SMALL_BUFF * 0.5 * OUT)
plane_copy1 = plane_copy.copy()
plane_copy1.stretch(0.01, 1, about_edge=DOWN)
plane_copy0 = plane_copy1.copy()
plane_copy0.stretch(0, 0, about_edge=LEFT)
words = TextMobject("2d input\\\\space")
words.scale(2)
words.move_to(plane.get_center() + SMALL_BUFF * OUT)
self.play(
Write(words),
self.camera.phi_tracker.set_value, 60 * DEGREES,
self.camera.theta_tracker.set_value, -90 * DEGREES,
run_time=1
)
self.play(
ReplacementTransform(plane_copy0, plane_copy1)
)
self.play(
ReplacementTransform(plane_copy1, plane_copy)
)
self.wait(2)
self.play(FadeOut(plane_copy))
self.input_plane_words = words
def reset_time_to_zero(self):
self.play(
self.t_tracker.set_value, 0,
VFadeOut(self.slicing_plane),
Restore(self.rod),
)
def show_changes_with_x(self):
alpha_tracker = ValueTracker(0)
line = always_redraw(
lambda: self.get_tangent_line(
self.graph, alpha_tracker.get_value(),
)
)
self.stop_ambient_camera_rotation()
self.play(
ShowCreation(line),
FadeOut(self.input_plane_words),
self.camera.phi_tracker.set_value, 80 * DEGREES,
self.camera.theta_tracker.set_value, -90 * DEGREES,
)
self.play(
alpha_tracker.set_value, 0.425,
run_time=5,
rate_func=bezier([0, 0, 1, 1]),
)
# Show dx and dT
p0 = line.point_from_proportion(0.3)
p2 = line.point_from_proportion(0.7)
p1 = np.array([p2[0], *p0[1:]])
dx_line = DashedLine(p0, p1)
dT_line = DashedLine(p1, p2)
dx = TexMobject("dx")
dT = TexMobject("dT")
VGroup(dx, dT).scale(0.7)
VGroup(dx, dT).rotate(90 * DEGREES, RIGHT)
dx.next_to(dx_line, IN, SMALL_BUFF)
dT.next_to(dT_line, RIGHT, SMALL_BUFF)
self.play(
ShowCreation(dx_line),
FadeInFrom(dx, LEFT)
)
self.wait()
self.play(
ShowCreation(dT_line),
FadeInFrom(dT, IN)
)
self.wait()
self.play(*map(FadeOut, [
line, dx_line, dT_line, dx, dT,
]))
def show_changes_with_t(self):
slices = self.graph_slices
slice_alpha = 0.075
graph = VMobject()
graph.set_points_smoothly([
gs.point_from_proportion(slice_alpha)
for gs in slices
])
graph.color_using_background_image("VerticalTempGradient")
graph.set_shade_in_3d(True)
alpha_tracker = ValueTracker(0)
line = always_redraw(
lambda: self.get_tangent_line(
graph, alpha_tracker.get_value(),
)
)
plane = Square()
plane.set_stroke(width=0)
plane.set_fill(WHITE, 0.1)
plane.set_shade_in_3d(True)
plane.rotate(90 * DEGREES, RIGHT)
plane.rotate(90 * DEGREES, OUT)
plane.set_height(10)
plane.set_depth(8, stretch=True)
plane.move_to(self.t_axis.n2p(0), IN + DOWN)
plane.shift(RIGHT)
self.play(
self.camera.theta_tracker.set_value, -20 * DEGREES,
self.camera.frame_center.shift, 4 * LEFT,
)
self.play(
ShowCreation(
graph.copy(),
remover=True
),
FadeInFrom(plane, 6 * DOWN, run_time=2),
VFadeIn(line),
ApplyMethod(
alpha_tracker.set_value, 1,
run_time=8,
),
)
self.add(graph)
self.begin_ambient_camera_rotation(-0.02)
self.camera.frame_center.add_updater(
lambda m, dt: m.shift(0.05 * dt * RIGHT)
)
self.play(
FadeOut(line),
FadeOut(plane),
)
self.wait(30) # Let rotate
self.t_graph = graph
#
def get_tangent_line(self, graph, alpha, d_alpha=0.001, length=2):
if alpha < 1 - d_alpha:
a1 = alpha
a2 = alpha + d_alpha
else:
a1 = alpha - d_alpha
a2 = alpha
p1 = graph.point_from_proportion(a1)
p2 = graph.point_from_proportion(a2)
line = Line(p1, p2, color=WHITE)
line.scale(
length / line.get_length()
)
line.move_to(p1)
return line
class TransitionToTempVsTime(ContrastXChangesToTChanges):
CONFIG = {
# "surface_resolution": 5,
# "graph_slice_step": 1,
}
def construct(self):
self.catchup_with_last_scene()
axes = self.original_axes
t_axis = self.t_axis
y_axis = axes.y_axis
x_axis = axes.x_axis
for mob in self.get_mobjects():
mob.clear_updaters()
self.stop_ambient_camera_rotation()
self.move_camera(
phi=90 * DEGREES,
theta=0 * DEGREES,
added_anims=[
Rotate(
y_axis, 90 * DEGREES,
axis=OUT,
about_point=y_axis.n2p(0),
),
FadeOut(VGroup(
self.graph_slices,
self.surface,
self.slicing_plane,
self.rod,
self.graph,
self.x_numbers,
self.x_axis_label,
self.t_graph,
)),
self.camera.frame_center.move_to, 5 * LEFT,
]
)
self.play(
VGroup(x_axis, self.input_plane).stretch,
0, 0, {"about_point": y_axis.n2p(0)},
)
self.play(
t_axis.time_label.scale, 1 / 1.5,
t_axis.time_label.next_to, t_axis, IN, MED_LARGE_BUFF,
t_axis.numbers.shift, 0.7 * IN,
)
self.wait()
def catchup_with_last_scene(self):
self.force_skipping()
self.add_axes()
self.add_graph()
self.add_rod()
self.rod_word = Point()
self.show_x_axis()
self.show_surface()
self.emphasize_dimensions_of_input_space()
self.reset_time_to_zero()
self.show_changes_with_x()
self.show_changes_with_t()
self.revert_to_original_skipping_status()
class ShowDelTermsAsTinyNudges(TransitionToTempVsTime):
CONFIG = {
# "surface_resolution": 5,
# "graph_slice_step": 1,
"tangent_line_length": 4,
}
def construct(self):
self.catchup_with_last_scene()
self.stop_camera()
self.show_del_t()
self.show_del_x()
def stop_camera(self):
self.stop_ambient_camera_rotation()
for mob in self.get_mobjects():
mob.clear_updaters()
def show_del_x(self):
x_tracker = ValueTracker(3)
dx_tracker = ValueTracker(0.5)
line_group = self.get_line_group(
self.graph,
x_tracker,
dx_tracker,
corner_index=0,
)
dx_line, dT_line, tan_line = line_group
del_x = TexMobject("\\partial x")
del_x.set_color(GREEN)
del_x.line = dx_line
del_x.direction = OUT
del_T = TexMobject("\\partial T")
del_T.line = dT_line
del_T.direction = RIGHT
syms = VGroup(del_T, del_x)
for sym in syms:
sym.add_updater(lambda m: m.set_width(
dx_line.get_length()
))
sym.rect = SurroundingRectangle(sym)
group = VGroup(sym, sym.rect)
group.rotate(90 * DEGREES, RIGHT)
for sym in syms:
sym.add_updater(lambda m: m.next_to(
m.line, m.direction, SMALL_BUFF,
))
sym.rect.move_to(sym)
self.move_camera(
phi=80 * DEGREES,
theta=-90 * DEGREES,
added_anims=[
self.camera.frame_center.move_to, ORIGIN,
],
)
for sym in reversed(syms):
self.play(
FadeInFrom(sym, -sym.direction),
ShowCreation(
sym.line.copy(),
remover=True
),
)
self.add(sym.line)
self.play(ShowCreation(tan_line))
for sym in syms:
self.play(
ShowCreationThenDestruction(sym.rect)
)
self.wait()
self.wait()
self.add(line_group)
self.play(
dx_tracker.set_value, 0.01,
run_time=5,
)
self.play(
FadeOut(syms),
FadeOut(line_group),
)
def show_del_t(self):
# Largely copy pasted from above.
# Reconsolidate if any of this will actually
# be used later.
t_tracker = ValueTracker(1)
dt_tracker = ValueTracker(1)
line_group = self.get_line_group(
self.t_graph, t_tracker, dt_tracker,
corner_index=1,
)
dt_line, dT_line, tan_line = line_group
del_t = TexMobject("\\partial t")
del_t.set_color(YELLOW)
del_t.line = dt_line
del_t.direction = OUT
del_T = TexMobject("\\partial T")
del_T.line = dT_line
del_T.direction = UP
syms = VGroup(del_T, del_t)
for sym in syms:
sym.rect = SurroundingRectangle(sym)
group = VGroup(sym, sym.rect)
group.rotate(90 * DEGREES, RIGHT)
group.rotate(90 * DEGREES, OUT)
sym.add_updater(lambda m: m.set_height(
0.8 * dT_line.get_length()
))
del_t.add_updater(lambda m: m.set_height(
min(0.5, m.line.get_length())
))
del_T.add_updater(lambda m: m.set_depth(
min(0.5, m.line.get_length())
))
for sym in syms:
sym.add_updater(lambda m: m.next_to(
m.line, m.direction, SMALL_BUFF,
))
sym.rect.move_to(sym)
self.move_camera(
phi=80 * DEGREES,
theta=-10 * DEGREES,
added_anims=[
self.camera.frame_center.move_to, 5 * LEFT,
],
)
for sym in reversed(syms):
self.play(
FadeInFrom(sym, -sym.direction),
ShowCreation(
sym.line.copy(),
remover=True
),
)
self.add(sym.line)
self.play(ShowCreation(tan_line))
for sym in syms:
self.play(
ShowCreationThenDestruction(sym.rect)
)
self.wait()
self.wait()
self.add(line_group)
self.play(
dt_tracker.set_value, 0.01,
run_time=5,
)
self.play(
FadeOut(syms),
FadeOut(line_group),
)
#
def get_line_group(self, graph, input_tracker, nudge_tracker, corner_index):
get_x = input_tracker.get_value
get_dx = nudge_tracker.get_value
def get_graph_point(x):
return graph.point_from_proportion(
inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
x,
)
)
def get_corner(p1, p2):
result = np.array(p1)
result[corner_index] = p2[corner_index]
return result
line_group = VGroup(
Line(color=WHITE),
Line(color=RED),
Line(color=WHITE, stroke_width=2),
)
def update_line_group(group):
dxl, dTl, tl = group
p0 = get_graph_point(get_x())
p2 = get_graph_point(get_x() + get_dx())
p1 = get_corner(p0, p2)
dxl.set_points_as_corners([p0, p1])
dTl.set_points_as_corners([p1, p2])
tl.set_points_as_corners([p0, p2])
tl.scale(
self.tangent_line_length / tl.get_length()
)
line_group.add_updater(update_line_group)
return line_group
class ShowCurvatureToRateOfChangeIntuition(ShowEvolvingTempGraphWithArrows):
CONFIG = {
"freq_amplitude_pairs": [
(1, 0.7),
(2, 1),
(3, 0.5),
(4, 0.3),
(5, 0.3),
(7, 0.2),
],
"arrow_xs": [0.7, 3.8, 4.6, 5.4, 6.2, 9.3],
"arrow_scale_factor": 0.2,
"max_magnitude": 1.0,
"wait_time": 20,
}
def let_play(self):
arrows = self.arrows
curves = VGroup(*[
self.get_mini_curve(
inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
x,
)
)
for x in self.arrow_xs
])
curves.set_stroke(WHITE, 5)
curve_words = VGroup()
for curve, arrow in zip(curves, arrows):
word = TextMobject("curve")
word.scale(0.7)
word.next_to(curve, arrow.get_vector()[1] * DOWN, SMALL_BUFF)
curve_words.add(word)
self.remove(arrows)
self.play(
ShowCreation(curves),
LaggedStartMap(FadeIn, curve_words),
self.y_label.set_fill, {"opacity": 0},
)
self.wait()
self.add(*arrows, curves)
self.play(LaggedStartMap(GrowArrow, arrows))
self.wait()
self.play(FadeOut(VGroup(curves, curve_words)))
self.add(arrows)
super().let_play()
def get_mini_curve(self, alpha, d_alpha=0.02):
result = VMobject()
result.pointwise_become_partial(
self.graph,
alpha - d_alpha,
alpha + d_alpha,
)
return result
class DiscreteSetup(ShowEvolvingTempGraphWithArrows):
CONFIG = {
"step_size": 1,
"rod_piece_size_ratio": 1 / 3,
"dashed_line_stroke_opacity": 1.0,
"dot_radius": DEFAULT_DOT_RADIUS,
"freq_amplitude_pairs": [
(1, 0.5),
(2, 1),
(3, 0.5),
(4, 0.3),
(5, 0.3),
(7, 0.2),
(21, 0.1),
# (41, 0.05),
],
}
def construct(self):
self.add_axes()
self.add_graph()
self.discretize()
self.let_time_pass()
self.show_nieghbor_rule()
self.focus_on_three_points()
self.show_difference_formula()
self.gut_check_new_interpretation()
self.write_second_difference()
self.emphasize_final_expression()
def add_axes(self):
super().add_axes()
self.axes.shift(MED_SMALL_BUFF * LEFT)
def add_graph(self):
points = self.get_points(time=0)
graph = VMobject()
graph.set_points_smoothly(points)
graph.color_using_background_image("VerticalTempGradient")
self.add(graph)
self.graph = graph
self.points = points
def discretize(self):
axes = self.axes
x_axis = axes.x_axis
graph = self.graph
piecewise_graph = CurvesAsSubmobjects(graph)
dots = self.get_dots()
v_lines = VGroup(*map(self.get_v_line, dots))
rod_pieces = VGroup()
for x in self.get_sample_inputs():
piece = Line(LEFT, RIGHT)
piece.set_width(
self.step_size * self.rod_piece_size_ratio
)
piece.move_to(axes.c2p(x, 0))
piece.set_color(
self.rod_point_to_color(piece.get_center())
)
rod_pieces.add(piece)
word = TextMobject("Discrete version")
word.scale(1.5)
word.next_to(x_axis, UP)
word.set_stroke(BLACK, 3, background=True)
self.remove(graph)
self.play(
ReplacementTransform(
piecewise_graph, dots,
),
Write(word, run_time=1)
)
self.add(v_lines, word)
self.play(
x_axis.fade, 0.8,
TransformFromCopy(
x_axis.tick_marks[1:],
rod_pieces,
),
LaggedStartMap(ShowCreation, v_lines)
)
self.play(FadeOut(word))
self.wait()
self.rod_pieces = rod_pieces
self.dots = dots
self.v_lines = v_lines
def let_time_pass(self):
dots = self.dots
t_tracker = ValueTracker(0)
t_tracker.add_updater(lambda m, dt: m.increment_value(dt))
self.add(t_tracker)
self.add_clock()
self.time_label.next_to(self.clock, DOWN)
self.time_label.add_updater(
lambda m: m.set_value(t_tracker.get_value())
)
dots.add_updater(lambda d: d.become(
self.get_dots(t_tracker.get_value())
))
run_time = 5
self.play(
ClockPassesTime(
self.clock,
run_time=run_time,
hours_passed=run_time,
),
)
t_tracker.clear_updaters()
t_tracker.set_value(run_time)
self.wait()
self.play(
t_tracker.set_value, 0,
FadeOut(self.clock),
FadeOut(self.time_label),
)
self.remove(t_tracker)
dots.clear_updaters()
def show_nieghbor_rule(self):
dots = self.dots
rod_pieces = self.rod_pieces
index = self.index = 2
p1, p2, p3 = rod_pieces[index:index + 3]
d1, d2, d3 = dots[index:index + 3]
point_label = TextMobject("Point")
neighbors_label = TextMobject("Neighbors")
words = VGroup(point_label, neighbors_label)
for word in words:
word.scale(0.7)
word.add_background_rectangle()
point_label.next_to(p2, DOWN)
neighbors_label.next_to(p2, UP, buff=1)
bottom = neighbors_label.get_bottom()
kw = {
"buff": 0.1,
"stroke_width": 2,
"tip_length": 0.15
}
arrows = VGroup(
Arrow(bottom, p1.get_center(), **kw),
Arrow(bottom, p3.get_center(), **kw),
)
arrows.set_color(WHITE)
dot = Dot()
dot.set_fill(GREY, opacity=0.2)
dot.replace(p2)
dot.scale(3)
self.play(
dot.scale, 0,
dot.set_opacity, 0,
FadeInFrom(point_label, DOWN)
)
self.play(
FadeInFrom(neighbors_label, DOWN),
*map(GrowArrow, arrows)
)
self.wait()
# Let d2 change
self.play(
d1.set_y, 3,
d3.set_y, 3,
)
def get_v():
return 0.25 * np.sum([
d1.get_y(),
-2 * d2.get_y(),
+ d3.get_y(),
])
v_vect_fader = ValueTracker(0)
v_vect = always_redraw(
lambda: Vector(
get_v() * UP,
color=temperature_to_color(
get_v(), -2, 2,
),
).shift(d2.get_center()).set_opacity(
v_vect_fader.get_value(),
)
)
d2.add_updater(
lambda d, dt: d.shift(
get_v() * dt * UP,
)
)
self.add(v_vect)
self.play(v_vect_fader.set_value, 1)
self.wait(3)
self.play(
d1.set_y, 0,
d3.set_y, 0,
)
self.wait(4)
self.play(FadeOut(VGroup(
point_label,
neighbors_label,
arrows
)))
self.v_vect = v_vect
self.example_pieces = VGroup(p1, p2, p3)
self.example_dots = VGroup(d1, d2, d3)
def focus_on_three_points(self):
dots = self.example_dots
d1, d2, d3 = dots
pieces = self.example_pieces
y_axis = self.axes.y_axis
x_labels, T_labels = [
VGroup(*[
TexMobject("{}_{}".format(s, i))
for i in [1, 2, 3]
]).scale(0.8)
for s in ("x", "T")
]
for xl, piece in zip(x_labels, pieces):
xl.next_to(piece, DOWN)
xl.add_background_rectangle()
for Tl, dot in zip(T_labels, dots):
Tl.dot = dot
Tl.add_updater(lambda m: m.next_to(
m.dot, RIGHT, SMALL_BUFF
))
Tl.add_background_rectangle()
T1, T2, T3 = T_labels
d2.movement_updater = d2.get_updaters()[0]
dots.clear_updaters()
self.remove(self.v_vect)
self.play(
ShowCreationThenFadeAround(pieces),
FadeOut(self.dots[:self.index]),
FadeOut(self.v_lines[:self.index]),
FadeOut(self.rod_pieces[:self.index]),
FadeOut(self.dots[self.index + 3:]),
FadeOut(self.v_lines[self.index + 3:]),
FadeOut(self.rod_pieces[self.index + 3:]),
)
self.play(LaggedStartMap(
FadeInFrom, x_labels,
lambda m: (m, LEFT),
lag_ratio=0.3,
run_time=2,
))
self.play(
d3.set_y, 1,
d2.set_y, 0.25,
d1.set_y, 0,
)
self.wait()
self.play(LaggedStart(*[
TransformFromCopy(xl, Tl)
for xl, Tl in zip(x_labels, T_labels)
], lag_ratio=0.3, run_time=2))
self.wait()
# Show lines
h_lines = VGroup(*map(self.get_h_line, dots))
hl1, hl2, hl3 = h_lines
average_pointer = ArrowTip(
start_angle=0,
length=0.2,
)
average_pointer.set_color(YELLOW)
average_pointer.stretch(0.25, 1)
average_pointer.add_updater(
lambda m: m.move_to(
0.5 * (hl1.get_start() + hl3.get_start()),
RIGHT
)
)
average_arrows = always_redraw(lambda: VGroup(*[
Arrow(
hl.get_start(),
average_pointer.get_right(),
color=WHITE,
buff=0.0,
)
for hl in [hl1, hl3]
]))
average_label = TexMobject(
"{T_1", "+", "T_3", "\\over", "2}"
)
average_label.scale(0.5)
average_label.add_updater(lambda m: m.next_to(
average_pointer, LEFT, SMALL_BUFF
))
average_rect = SurroundingRectangle(average_label)
average_rect.add_updater(
lambda m: m.move_to(average_label)
)
average_words = TextMobject("Neighbor\\\\average")
average_words.match_width(average_rect)
average_words.match_color(average_rect)
average_words.add_updater(
lambda m: m.next_to(average_rect, UP, SMALL_BUFF)
)
mini_T1 = average_label.get_part_by_tex("T_1")
mini_T3 = average_label.get_part_by_tex("T_3")
for mini, line in (mini_T1, hl1), (mini_T3, hl3):
mini.save_state()
mini.next_to(line, LEFT, SMALL_BUFF)
self.add(hl1, hl3, T_labels)
y_axis.remove(y_axis.numbers)
self.play(
GrowFromPoint(hl1, hl1.get_end()),
GrowFromPoint(hl3, hl3.get_end()),
TransformFromCopy(
T1, mini_T1,
),
TransformFromCopy(
T3, mini_T3,
),
FadeOut(y_axis.numbers),
y_axis.set_stroke, {"width": 1},
)
self.play(
FadeIn(average_pointer),
Restore(mini_T1),
Restore(mini_T3),
FadeIn(average_label[1]),
FadeIn(average_label[3:]),
*map(GrowArrow, average_arrows)
)
self.add(average_arrows, average_label)
self.play(
ShowCreation(average_rect),
FadeIn(average_words),
)
self.play(
GrowFromPoint(hl2, hl2.get_end())
)
self.wait()
# Show formula
formula = TexMobject(
"\\left(",
"{T_1", "+", "T_3", "\\over", "2}",
"-", "T_2",
"\\right)"
)
formula.to_corner(UR, buff=MED_LARGE_BUFF)
formula.shift(1.7 * LEFT)
brace = Brace(formula, DOWN)
diff_value = DecimalNumber(include_sign=True)
diff_value.add_updater(lambda m: m.set_value(
y_axis.p2n(average_pointer.get_right()) -
y_axis.p2n(d2.get_center())
))
diff_value.next_to(brace, DOWN)
self.play(
ReplacementTransform(
average_label.deepcopy(),
formula[1:1 + len(average_label)]
),
TransformFromCopy(T2, formula[-2]),
FadeIn(formula[-3]),
FadeIn(formula[-1]),
FadeIn(formula[0]),
GrowFromCenter(brace),
FadeIn(diff_value)
)
self.wait()
# Changes
self.play(FadeIn(self.v_vect))
d2.add_updater(d2.movement_updater)
self.wait(5)
self.play(
d3.set_y, 3,
d1.set_y, 2.5,
d2.set_y, -2,
)
self.wait(5)
self.play(
d3.set_y, 1,
d1.set_y, -1,
)
self.wait(8)
# Show derivative
lhs = TexMobject(
"{dT_2", "\\over", "dt}", "=", "\\alpha"
)
dt = lhs.get_part_by_tex("dt")
alpha = lhs.get_part_by_tex("\\alpha")
lhs.next_to(formula, LEFT, SMALL_BUFF)
self.play(Write(lhs))
self.play(ShowCreationThenFadeAround(dt))
self.wait()
self.play(ShowCreationThenFadeAround(alpha))
self.wait()
self.play(
FadeOut(brace),
FadeOut(diff_value),
)
self.lhs = lhs
self.rhs = formula
def show_difference_formula(self):
lhs = self.lhs
rhs = self.rhs
d1, d2, d3 = self.example_dots
new_rhs = TexMobject(
"=",
"{\\alpha", "\\over", "2}",
"\\left(",
"(", "T_3", "-", "T_2", ")",
"-",
"(", "T_2", "-", "T_1", ")",
"\\right)"
)
big_parens = VGroup(
new_rhs.get_part_by_tex("\\left("),
new_rhs.get_part_by_tex("\\right)"),
)
for paren in big_parens:
paren.scale(2)
new_rhs.next_to(rhs, DOWN)
new_rhs.align_to(lhs.get_part_by_tex("="), LEFT)
def p2p_anim(mob1, mob2, tex, index=0):
return TransformFromCopy(
mob1.get_parts_by_tex(tex)[index],
mob2.get_parts_by_tex(tex)[index],
)
self.play(
p2p_anim(lhs, new_rhs, "="),
p2p_anim(rhs, new_rhs, "\\left("),
p2p_anim(rhs, new_rhs, "\\right)"),
p2p_anim(lhs, new_rhs, "\\alpha"),
p2p_anim(rhs, new_rhs, "\\over"),
p2p_anim(rhs, new_rhs, "2"),
)
self.play(
p2p_anim(rhs, new_rhs, "T_3"),
p2p_anim(rhs, new_rhs, "-"),
p2p_anim(rhs, new_rhs, "T_2"),
FadeIn(new_rhs.get_parts_by_tex("(")[1]),
FadeIn(new_rhs.get_parts_by_tex(")")[0]),
)
self.play(
p2p_anim(rhs, new_rhs, "T_2", -1),
p2p_anim(rhs, new_rhs, "-", -1),
p2p_anim(rhs, new_rhs, "T_1"),
FadeIn(new_rhs.get_parts_by_tex("-")[1]),
FadeIn(new_rhs.get_parts_by_tex("(")[2]),
FadeIn(new_rhs.get_parts_by_tex(")")[1]),
)
self.wait()
self.rhs2 = new_rhs
# Show deltas
T1_index = new_rhs.index_of_part_by_tex("T_1")
T3_index = new_rhs.index_of_part_by_tex("T_3")
diff1 = new_rhs[T1_index - 2:T1_index + 1]
diff2 = new_rhs[T3_index:T3_index + 3]
brace1 = Brace(diff1, DOWN, buff=SMALL_BUFF)
brace2 = Brace(diff2, DOWN, buff=SMALL_BUFF)
delta_T1 = TexMobject("\\Delta T_1")
delta_T1.next_to(brace1, DOWN, SMALL_BUFF)
delta_T2 = TexMobject("\\Delta T_2")
delta_T2.next_to(brace2, DOWN, SMALL_BUFF)
minus = TexMobject("-")
minus.move_to(Line(
delta_T1.get_right(),
delta_T2.get_left(),
))
braces = VGroup(brace1, brace2)
deltas = VGroup(delta_T1, delta_T2)
kw = {
"direction": LEFT,
"buff": SMALL_BUFF,
"min_num_quads": 2,
}
lil_brace1 = always_redraw(lambda: Brace(
Line(d1.get_left(), d2.get_left()), **kw
))
lil_brace2 = always_redraw(lambda: Brace(
Line(d2.get_left(), d3.get_left()), **kw
))
lil_braces = VGroup(lil_brace1, lil_brace2)
lil_delta_T1 = delta_T1.copy()
lil_delta_T2 = delta_T2.copy()
lil_deltas = VGroup(lil_delta_T1, lil_delta_T2)
for brace, delta in zip(lil_braces, lil_deltas):
delta.brace = brace
delta.add_updater(lambda d: d.next_to(
d.brace, LEFT, SMALL_BUFF,
))
delta_T1.set_color(BLUE)
lil_delta_T1.set_color(BLUE)
delta_T2.set_color(RED)
lil_delta_T2.set_color(RED)
double_difference_brace = Brace(deltas, DOWN)
double_difference_words = TextMobject(
"Difference of differences"
)
double_difference_words.next_to(
double_difference_brace, DOWN
)
self.play(
GrowFromCenter(brace1),
GrowFromCenter(lil_brace1),
FadeIn(delta_T1),
FadeIn(lil_delta_T1),
)
self.wait()
self.play(
GrowFromCenter(brace2),
GrowFromCenter(lil_brace2),
FadeIn(delta_T2),
FadeIn(lil_delta_T2),
)
self.wait()
self.play(
Write(minus),
GrowFromCenter(double_difference_brace),
Write(double_difference_words),
)
self.wait()
self.braces = braces
self.deltas = deltas
self.delta_minus = minus
self.lil_braces = lil_braces
self.lil_deltas = lil_deltas
self.double_difference_brace = double_difference_brace
self.double_difference_words = double_difference_words
def gut_check_new_interpretation(self):
lil_deltas = self.lil_deltas
d1, d2, d3 = self.example_dots
self.play(ShowCreationThenFadeAround(lil_deltas[0]))
self.play(ShowCreationThenFadeAround(lil_deltas[1]))
self.wait()
self.play(
d2.shift, MED_SMALL_BUFF * UP,
rate_func=there_and_back,
)
self.wait()
self.play(
d3.set_y, 3,
d1.set_y, -0.5,
)
self.wait(5)
self.play(
d3.set_y, 1.5,
d1.set_y, -2,
)
self.wait(5)
def write_second_difference(self):
dd_word = self.double_difference_words
delta_delta = TexMobject("\\Delta \\Delta T_1")
delta_delta.set_color(MAROON_B)
delta_delta.move_to(dd_word, UP)
second_difference_word = TextMobject(
"``Second difference''"
)
second_difference_word.next_to(delta_delta, DOWN)
self.play(
FadeOutAndShift(dd_word, UP),
FadeInFrom(delta_delta, UP),
)
self.wait()
self.play(
Write(second_difference_word),
)
self.wait()
# Random play
d1, d2, d3 = self.example_dots
self.play(
d3.set_y, 3,
d1.set_y, -0.5,
)
self.wait(5)
self.play(
d3.set_y, 1.5,
d1.set_y, -2,
)
self.wait(5)
self.delta_delta = delta_delta
self.second_difference_word = second_difference_word
def emphasize_final_expression(self):
lhs = self.lhs
rhs = self.rhs
rhs2 = self.rhs2
old_dd = self.delta_delta
dd = old_dd.copy()
old_ao2 = rhs2[1:4]
ao2 = old_ao2.copy()
new_lhs = lhs[:-1]
full_rhs = VGroup(
lhs[-1],
lhs[-2].copy(),
rhs,
rhs2,
self.braces,
self.deltas,
self.delta_minus,
self.double_difference_brace,
old_dd,
self.second_difference_word,
)
new_rhs = VGroup(ao2, dd)
new_rhs.arrange(RIGHT, buff=SMALL_BUFF)
new_rhs.next_to(new_lhs, RIGHT)
self.play(
full_rhs.to_edge, DOWN, {"buff": LARGE_BUFF},
)
self.play(
TransformFromCopy(old_ao2, ao2),
TransformFromCopy(old_dd, dd),
)
self.play(
ShowCreationThenFadeAround(
VGroup(new_lhs, new_rhs)
)
)
self.wait()
#
def get_sample_inputs(self):
return np.arange(
self.graph_x_min,
self.graph_x_max + self.step_size,
self.step_size,
)
def get_points(self, time=0):
return [
self.axes.c2p(x, self.temp_func(x, t=time))
for x in self.get_sample_inputs()
]
def get_dots(self, time=0):
points = self.get_points(time)
dots = VGroup(*[
Dot(
point,
radius=self.dot_radius
)
for point in points
])
dots.color_using_background_image("VerticalTempGradient")
return dots
def get_dot_dashed_line(self, dot, index, color=False):
direction = np.zeros(3)
direction[index] = -1
def get_line():
p1 = dot.get_edge_center(direction)
p0 = np.array(p1)
p0[index] = self.axes.c2p(0, 0)[index]
result = DashedLine(
p0, p1,
stroke_width=2,
color=WHITE,
stroke_opacity=self.dashed_line_stroke_opacity,
)
if color:
result.color_using_background_image(
"VerticalTempGradient"
)
return result
return always_redraw(get_line)
def get_h_line(self, dot):
return self.get_dot_dashed_line(dot, 0, True)
def get_v_line(self, dot):
return self.get_dot_dashed_line(dot, 1)
class ShowFinitelyManyX(DiscreteSetup):
def construct(self):
self.setup_axes()
axes = self.axes
axes.fade(1)
points = [
axes.c2p(x, 0)
for x in self.get_sample_inputs()[1:]
]
x_labels = VGroup(*[
TexMobject("x_{}".format(i)).next_to(
p, DOWN
)
for i, p in enumerate(points)
])
self.play(LaggedStartMap(
FadeInFromLarge, x_labels
))
self.play(LaggedStartMap(FadeOut, x_labels))
self.wait()
class DiscreteGraphStillImage1(DiscreteSetup):
CONFIG = {
"step_size": 1,
}
def construct(self):
self.add_axes()
self.add_graph()
self.discretize()
class DiscreteGraphStillImageFourth(DiscreteGraphStillImage1):
CONFIG = {
"step_size": 0.25,
}
class DiscreteGraphStillImageTenth(DiscreteGraphStillImage1):
CONFIG = {
"step_size": 0.1,
"dashed_line_stroke_opacity": 0.25,
"dot_radius": 0.04,
}
class DiscreteGraphStillImageHundredth(DiscreteGraphStillImage1):
CONFIG = {
"step_size": 0.01,
"dashed_line_stroke_opacity": 0.1,
"dot_radius": 0.01,
}
class TransitionToContinuousCase(DiscreteSetup):
CONFIG = {
"step_size": 0.1,
"tangent_line_length": 3,
"wait_time": 30,
}
def construct(self):
self.add_axes()
self.add_graph()
self.show_temperature_difference()
self.show_second_derivative()
self.show_curvature_examples()
self.show_time_changes()
def add_graph(self):
self.setup_graph()
self.play(
ShowCreation(
self.graph,
run_time=3,
)
)
self.wait()
def show_temperature_difference(self):
x_tracker = ValueTracker(2)
dx_tracker = ValueTracker(1)
line_group = self.get_line_group(
x_tracker,
dx_tracker,
)
dx_line, dT_line, tan_line, dx_sym, dT_sym = line_group
tan_line.set_stroke(width=0)
brace = Brace(dx_line, UP)
fixed_distance = TextMobject("Fixed\\\\distance")
fixed_distance.scale(0.7)
fixed_distance.next_to(brace, UP)
delta_T = TexMobject("\\Delta T")
delta_T.move_to(dT_sym, LEFT)
self.play(
ShowCreation(VGroup(dx_line, dT_line)),
FadeInFrom(delta_T, LEFT)
)
self.play(
GrowFromCenter(brace),
FadeInFromDown(fixed_distance),
)
self.wait()
self.play(
FadeOut(delta_T, UP),
FadeIn(dT_sym, DOWN),
FadeOut(brace, UP),
FadeOut(fixed_distance, UP),
FadeIn(dx_sym, DOWN),
)
self.add(line_group)
self.play(
dx_tracker.set_value, 0.01,
run_time=5
)
self.wait()
self.play(
dx_tracker.set_value, 0.3,
)
# Show rate of change
to_zero = TexMobject("\\rightarrow 0")
to_zero.match_height(dT_sym)
to_zero.next_to(dT_sym, buff=SMALL_BUFF)
ratio = TexMobject(
"{\\partial T", "\\over", "\\partial x}"
)
ratio[0].match_style(dT_sym)
ratio.to_edge(UP)
self.play(ShowCreationThenFadeAround(
dT_sym,
surrounding_rectangle_config={
"buff": 0.05,
"stroke_width": 1,
}
))
self.play(GrowFromPoint(to_zero, dT_sym.get_right()))
self.wait()
self.play(
TransformFromCopy(
dT_sym,
ratio.get_part_by_tex("\\partial T")
),
TransformFromCopy(
dx_sym,
ratio.get_part_by_tex("\\partial x")
),
Write(ratio.get_part_by_tex("\\over"))
)
self.play(
ShowCreation(
tan_line.copy().set_stroke(width=2),
remover=True
),
FadeOut(to_zero),
)
tan_line.set_stroke(width=2)
self.wait()
# Look at neighbors
x0 = x_tracker.get_value()
dx = dx_tracker.get_value()
v_line, lv_line, rv_line = v_lines = VGroup(*[
self.get_v_line(x)
for x in [x0, x0 - dx, x0 + dx]
])
v_lines[1:].set_color(BLUE)
self.play(ShowCreation(v_line))
self.play(
TransformFromCopy(v_line, lv_line),
TransformFromCopy(v_line, rv_line),
)
self.wait()
self.play(
FadeOut(v_lines[1:]),
ApplyMethod(
dx_tracker.set_value, 0.01,
run_time=2
),
)
self.line_group = line_group
self.deriv = ratio
self.x_tracker = x_tracker
self.dx_tracker = dx_tracker
self.v_line = v_line
def show_second_derivative(self):
x_tracker = self.x_tracker
deriv = self.deriv
v_line = self.v_line
deriv_of_deriv = TexMobject(
"{\\partial",
"\\left(",
"{\\partial T", "\\over", "\\partial x}",
"\\right)",
"\\over",
"\\partial x}"
)
deriv_of_deriv.set_color_by_tex("\\partial T", RED)
deriv_of_deriv.to_edge(UP)
dT_index = deriv_of_deriv.index_of_part_by_tex("\\partial T")
inner_deriv = deriv_of_deriv[dT_index:dT_index + 3]
self.play(
ReplacementTransform(deriv, inner_deriv),
Write(VGroup(*filter(
lambda m: m not in inner_deriv,
deriv_of_deriv,
)))
)
v_line.add_updater(lambda m: m.become(
self.get_v_line(x_tracker.get_value())
))
for change in [-0.1, 0.1]:
self.play(
x_tracker.increment_value, change,
run_time=3
)
# Write second deriv
second_deriv = TexMobject(
"{\\partial^2 T", "\\over", "\\partial x^2}"
)
second_deriv[0].set_color(RED)
eq = TexMobject("=")
eq.next_to(deriv_of_deriv, RIGHT)
second_deriv.next_to(eq, RIGHT)
second_deriv.align_to(deriv_of_deriv, DOWN)
eq.match_y(second_deriv.get_part_by_tex("\\over"))
self.play(Write(eq))
self.play(
TransformFromCopy(
deriv_of_deriv.get_parts_by_tex("\\partial")[:2],
second_deriv.get_parts_by_tex("\\partial^2 T"),
),
)
self.play(
Write(second_deriv.get_part_by_tex("\\over")),
TransformFromCopy(
deriv_of_deriv.get_parts_by_tex("\\partial x"),
second_deriv.get_parts_by_tex("\\partial x"),
),
)
self.wait()
def show_curvature_examples(self):
x_tracker = self.x_tracker
v_line = self.v_line
line_group = self.line_group
x_tracker.set_value(3.6)
self.wait()
self.play(
x_tracker.set_value, 3.8,
run_time=4,
)
self.wait()
x_tracker.set_value(6.2)
self.wait()
self.play(
x_tracker.set_value, 6.4,
run_time=4,
)
self.wait()
#
dx = 0.2
neighbor_lines = always_redraw(lambda: VGroup(*[
self.get_v_line(
x_tracker.get_value() + u * dx,
line_class=Line,
)
for u in [-1, 1]
]))
neighbor_lines.set_color(BLUE)
self.play(FadeOut(line_group))
self.play(*[
TransformFromCopy(v_line, nl)
for nl in neighbor_lines
])
self.add(neighbor_lines)
self.play(
x_tracker.set_value, 5,
run_time=5,
rate_func=lambda t: smooth(t, 3)
)
v_line.clear_updaters()
self.play(
FadeOut(v_line),
FadeOut(neighbor_lines),
)
self.wait()
def show_time_changes(self):
self.setup_clock()
graph = self.graph
time_label = self.time_label
clock = self.clock
time_label.next_to(clock, DOWN)
graph.add_updater(self.update_graph)
time_label.add_updater(
lambda d, dt: d.increment_value(dt)
)
self.add(time_label)
self.add_arrows()
self.play(
ClockPassesTime(
clock,
run_time=self.wait_time,
hours_passed=self.wait_time,
),
)
#
def get_v_line(self, x, line_class=DashedLine, stroke_width=2):
axes = self.axes
graph = self.graph
line = line_class(
axes.c2p(x, 0),
graph.point_from_proportion(
inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
x,
)
),
stroke_width=stroke_width,
)
return line
def get_line_group(self,
x_tracker,
dx_tracker,
dx_tex="\\partial x",
dT_tex="\\partial T",
max_sym_width=0.5,
):
graph = self.graph
get_x = x_tracker.get_value
get_dx = dx_tracker.get_value
dx_line = Line(color=WHITE)
dT_line = Line(color=RED)
tan_line = Line(color=WHITE)
lines = VGroup(dx_line, dT_line, tan_line)
lines.set_stroke(width=2)
dx_sym = TexMobject(dx_tex)
dT_sym = TexMobject(dT_tex)
dT_sym.match_color(dT_line)
syms = VGroup(dx_sym, dT_sym)
group = VGroup(*lines, *syms)
def update_group(group):
dxl, dTl, tanl, dxs, dTs = group
x = get_x()
dx = get_dx()
p0, p2 = [
graph.point_from_proportion(
inverse_interpolate(
self.graph_x_min,
self.graph_x_max,
x
)
)
for x in [x, x + dx]
]
p1 = np.array([p2[0], *p0[1:]])
dxl.put_start_and_end_on(p0, p1)
dTl.put_start_and_end_on(p1, p2)
tanl.put_start_and_end_on(p0, p2)
tanl.scale(
self.tangent_line_length /
tanl.get_length()
)
dxs.match_width(dxl)
dTs.set_height(0.7 * dTl.get_height())
for sym in dxs, dTs:
if sym.get_width() > max_sym_width:
sym.set_width(max_sym_width)
dxs.next_to(
dxl, -dTl.get_vector(), SMALL_BUFF,
)
dTs.next_to(
dTl, dxl.get_vector(), SMALL_BUFF,
)
group.add_updater(update_group)
return group
class ShowManyVLines(TransitionToContinuousCase):
CONFIG = {
"wait_time": 20,
"max_denom": 10,
"x_step": 0.025,
}
def construct(self):
self.add_axes()
self.add_graph()
self.add_v_lines()
self.show_time_changes()
def add_arrows(self):
pass
def add_v_lines(self):
axes = self.axes
v_lines = always_redraw(lambda: VGroup(*[
self.get_v_line(
x,
line_class=Line,
stroke_width=0.5,
)
for x in np.arange(0, 10, self.x_step)
]))
group = VGroup(*v_lines)
x_pointer = ArrowTip(start_angle=PI / 2)
x_pointer.set_color(WHITE)
x_pointer.next_to(axes.c2p(0, 0), DOWN, buff=0)
x_eq = VGroup(
TexMobject("x="),
DecimalNumber(0)
)
x_eq.add_updater(
lambda m: m.arrange(RIGHT, buff=SMALL_BUFF)
)
x_eq.add_updater(
lambda m: m[1].set_value(axes.x_axis.p2n(x_pointer.get_top()))
)
x_eq.add_updater(lambda m: m.next_to(
x_pointer, DOWN, SMALL_BUFF,
submobject_to_align=x_eq[0]
))
self.add(x_pointer, x_eq)
self.play(
Write(
group,
remover=True,
lag_ratio=self.x_step / 2,
run_time=6,
),
ApplyMethod(
x_pointer.next_to,
axes.c2p(10, 0),
DOWN, {"buff": 0},
rate_func=linear,
run_time=5,
),
)
self.add(v_lines)
x_eq.clear_updaters()
self.play(
FadeOut(x_eq),
FadeOut(x_pointer),
)
class ShowNewtonsLawGraph(Scene):
CONFIG = {
"k": 0.2,
"initial_water_temp": 80,
"room_temp": 20,
"delta_T_color": YELLOW,
}
def construct(self):
self.setup_axes()
self.show_temperatures()
self.show_graph()
self.show_equation()
self.talk_through_examples()
def setup_axes(self):
axes = Axes(
x_min=0,
x_max=10,
y_min=0,
y_max=100,
y_axis_config={
"unit_size": 0.06,
"tick_frequency": 10,
},
center_point=5 * LEFT + 2.5 * DOWN
)
x_axis = axes.x_axis
y_axis = axes.y_axis
y_axis.add_numbers(*range(20, 100, 20))
x_axis.add_numbers(*range(1, 11))
x_axis.label = TextMobject("Time")
x_axis.label.next_to(x_axis, DOWN, MED_SMALL_BUFF)
y_axis.label = TexMobject("\\text{Temperature}")
y_axis.label.next_to(y_axis, RIGHT, buff=SMALL_BUFF)
y_axis.label.align_to(axes, UP)
for axis in [x_axis, y_axis]:
axis.add(axis.label)
self.add(axes)
self.axes = axes
def show_temperatures(self):
axes = self.axes
water_dot = Dot()
water_dot.color_using_background_image("VerticalTempGradient")
water_dot.move_to(axes.c2p(0, self.initial_water_temp))
room_line = DashedLine(
axes.c2p(0, self.room_temp),
axes.c2p(10, self.room_temp),
)
room_line.set_color(BLUE)
room_line.color_using_background_image("VerticalTempGradient")
water_arrow = Vector(LEFT, color=WHITE)
water_arrow.next_to(water_dot, RIGHT, SMALL_BUFF)
water_words = TextMobject(
"Initial water\\\\temperature"
)
water_words.scale(0.7)
water_words.next_to(water_arrow, RIGHT)
room_words = TextMobject("Room temperature")
room_words.scale(0.7)
room_words.next_to(room_line, DOWN, SMALL_BUFF)
self.play(
FadeInFrom(water_dot, RIGHT),
GrowArrow(water_arrow),
Write(water_words),
run_time=1,
)
self.play(ShowCreation(room_line))
self.play(FadeInFromDown(room_words))
self.wait()
self.set_variables_as_attrs(
water_dot,
water_arrow,
water_words,
room_line,
room_words,
)
def show_graph(self):
axes = self.axes
water_dot = self.water_dot
k = self.k
rt = self.room_temp
t0 = self.initial_water_temp
graph = axes.get_graph(
lambda t: rt + (t0 - rt) * np.exp(-k * t)
)
graph.color_using_background_image("VerticalTempGradient")
def get_x():
return axes.x_axis.p2n(water_dot.get_center())
brace_line = always_redraw(lambda: Line(
axes.c2p(get_x(), rt),
water_dot.get_center(),
stroke_width=0,
))
brace = always_redraw(
lambda: Brace(
brace_line, RIGHT, buff=SMALL_BUFF
)
)
delta_T = TexMobject("\\Delta T")
delta_T.set_color(self.delta_T_color)
delta_T.add_updater(lambda m: m.next_to(
brace, RIGHT, SMALL_BUFF
))
self.add(brace_line)
self.play(
GrowFromCenter(brace),
Write(delta_T),
)
self.play(
ShowCreation(graph),
UpdateFromFunc(
water_dot,
lambda m: m.move_to(graph.get_end())
),
run_time=10,
rate_func=linear,
)
self.wait()
self.graph = graph
self.brace = brace
self.delta_T = delta_T
def show_equation(self):
delta_T = self.delta_T
equation = TexMobject(
"{d ({\\Delta T}) \\over dt} = -k \\cdot {\\Delta T}",
tex_to_color_map={
"{\\Delta T}": self.delta_T_color,
"-k": WHITE,
"=": WHITE,
}
)
equation.to_corner(UR)
equation.shift(LEFT)
delta_T_parts = equation.get_parts_by_tex("\\Delta T")
eq_i = equation.index_of_part_by_tex("=")
deriv = equation[:eq_i]
prop_to = equation.get_part_by_tex("-k")
parts = VGroup(deriv, prop_to, delta_T_parts[1])
words = TextMobject(
"Rate of change",
"is proportional to",
"itself",
)
words.scale(0.7)
words.next_to(equation, DOWN)
colors = [BLUE, WHITE, YELLOW]
for part, word, color in zip(parts, words, colors):
part.word = word
word.set_color(color)
word.save_state()
words[0].next_to(parts[0], DOWN)
self.play(
TransformFromCopy(
VGroup(delta_T),
delta_T_parts,
),
Write(VGroup(*filter(
lambda p: p not in delta_T_parts,
equation
)))
)
rects = VGroup()
for part in parts:
rect = SurroundingRectangle(
part,
color=part.word.get_color(),
buff=SMALL_BUFF,
stroke_width=2,
)
anims = [
ShowCreation(rect),
FadeIn(part.word),
]
if part is parts[1]:
anims.append(Restore(words[0]))
self.play(*anims)
rects.add(rect)
self.play(FadeOut(rects, lag_ratio=0.2))
self.equation = equation
self.equation_words = words
def talk_through_examples(self):
dot = self.water_dot
graph = self.graph
self.play(
MoveAlongPath(
dot, graph,
rate_func=lambda t: smooth(1 - t),
run_time=2,
)
)
#
def get_slope_line(self, graph, x):
pass