mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
34d1d27c56
11 changed files with 2808 additions and 189 deletions
|
@ -1,13 +1,43 @@
|
|||
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"
|
||||
SCENES_IN_ORDER = [
|
||||
LastChapterWrapper,
|
||||
ThreeConstraints,
|
||||
OceanOfPossibilities,
|
||||
# TODO
|
||||
ThreeMainObservations,
|
||||
BreakDownAFunction,
|
||||
SineCurveIsUnrealistic,
|
||||
AnalyzeSineCurve,
|
||||
EquationAboveSineAnalysis,
|
||||
ExponentialDecay,
|
||||
InvestmentGrowth,
|
||||
GrowingPileOfMoney,
|
||||
CarbonDecayCurve,
|
||||
CarbonDecayingInMammoth,
|
||||
SineWaveScaledByExp,
|
||||
ShowSinExpDerivatives,
|
||||
IfOnly,
|
||||
BoundaryConditionInterlude,
|
||||
BoundaryConditionReference,
|
||||
GiantCross,
|
||||
SimulateRealSineCurve,
|
||||
SimulateLinearGraph,
|
||||
|
||||
# SimpleCosExpGraph,
|
||||
# AddMultipleSolutions,
|
||||
# IveHeardOfThis,
|
||||
# FourierSeriesOfLineIllustration,
|
||||
# InFouriersShoes,
|
||||
]
|
||||
|
||||
PART_4_SCENES = [
|
||||
FourierSeriesIllustraiton,
|
||||
FourierNameIntro,
|
||||
CircleAnimationOfF,
|
||||
LastChapterWrapper,
|
||||
ThreeMainObservations,
|
||||
SimpleSinExpGraph,
|
||||
]
|
||||
|
|
|
@ -349,16 +349,16 @@ class BringTwoRodsTogether(Scene):
|
|||
if (0 < i < len(points) - 1):
|
||||
second_deriv = d2y / (dx**2)
|
||||
else:
|
||||
second_deriv = 0.5 * d2y / dx
|
||||
second_deriv = 0
|
||||
second_deriv = d2y / dx
|
||||
# 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)
|
||||
# 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
|
||||
|
@ -374,6 +374,15 @@ class BringTwoRodsTogether(Scene):
|
|||
)[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)
|
||||
|
||||
|
@ -407,7 +416,7 @@ class BringTwoRodsTogether(Scene):
|
|||
self.rod_point_to_color(piece.get_right()),
|
||||
])
|
||||
|
||||
def rod_point_to_color(self, point):
|
||||
def rod_point_to_graph_y(self, point):
|
||||
axes = self.axes
|
||||
x = axes.x_axis.p2n(point)
|
||||
|
||||
|
@ -417,11 +426,16 @@ class BringTwoRodsTogether(Scene):
|
|||
self.graph_x_max,
|
||||
x,
|
||||
)
|
||||
y = axes.y_axis.p2n(
|
||||
return axes.y_axis.p2n(
|
||||
graph.point_from_proportion(alpha)
|
||||
)
|
||||
return temperature_to_color(
|
||||
(y - 45) / 45
|
||||
|
||||
def y_to_color(self, y):
|
||||
return temperature_to_color((y - 45) / 45)
|
||||
|
||||
def rod_point_to_color(self, point):
|
||||
return self.y_to_color(
|
||||
self.rod_point_to_graph_y(point)
|
||||
)
|
||||
|
||||
|
||||
|
@ -467,7 +481,10 @@ class ShowEvolvingTempGraphWithArrows(BringTwoRodsTogether):
|
|||
self.time_label.next_to(self.clock, DOWN)
|
||||
|
||||
def add_rod(self):
|
||||
rod = self.rod = self.get_rod(0, 10)
|
||||
rod = self.rod = self.get_rod(
|
||||
self.graph_x_min,
|
||||
self.graph_x_max,
|
||||
)
|
||||
self.add(rod)
|
||||
|
||||
def add_arrows(self):
|
||||
|
|
140
active_projects/ode/part3/pi_creature_scenes.py
Normal file
140
active_projects/ode/part3/pi_creature_scenes.py
Normal file
|
@ -0,0 +1,140 @@
|
|||
from manimlib.imports import *
|
||||
from active_projects.ode.part2.wordy_scenes import *
|
||||
|
||||
|
||||
class IveHeardOfThis(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
point = VectorizedPoint()
|
||||
point.move_to(3 * RIGHT + 2 * UP)
|
||||
self.student_says(
|
||||
"I've heard\\\\", "of this!",
|
||||
student_index=1,
|
||||
target_mode="hooray",
|
||||
bubble_kwargs={
|
||||
"height": 3,
|
||||
"width": 3,
|
||||
"direction": RIGHT,
|
||||
},
|
||||
run_time=1,
|
||||
)
|
||||
self.change_student_modes(
|
||||
"thinking", "hooray", "thinking",
|
||||
look_at_arg=point,
|
||||
added_anims=[self.teacher.change, "happy"]
|
||||
)
|
||||
self.wait(3)
|
||||
self.student_says(
|
||||
"But who\\\\", "cares?",
|
||||
student_index=1,
|
||||
target_mode="maybe",
|
||||
bubble_kwargs={
|
||||
"direction": RIGHT,
|
||||
"width": 3,
|
||||
"height": 3,
|
||||
},
|
||||
run_time=1,
|
||||
)
|
||||
self.change_student_modes(
|
||||
"pondering", "maybe", "pondering",
|
||||
look_at_arg=point,
|
||||
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):
|
||||
self.student_says(
|
||||
"But that would\\\\never happen!",
|
||||
student_index=1,
|
||||
bubble_kwargs={
|
||||
"direction": RIGHT,
|
||||
"height": 3,
|
||||
"width": 4,
|
||||
},
|
||||
target_mode="angry"
|
||||
)
|
||||
self.change_student_modes(
|
||||
"guilty", "angry", "hesitant",
|
||||
added_anims=[
|
||||
self.teacher.change, "tease"
|
||||
]
|
||||
)
|
||||
self.wait(3)
|
||||
self.play(
|
||||
RemovePiCreatureBubble(self.students[1]),
|
||||
self.teacher.change, "raise_right_hand"
|
||||
)
|
||||
self.change_all_student_modes(
|
||||
"pondering",
|
||||
look_at_arg=3 * UP,
|
||||
)
|
||||
self.wait(5)
|
||||
|
||||
|
||||
|
||||
class IfOnly(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says(
|
||||
"If only!",
|
||||
target_mode="angry"
|
||||
)
|
||||
self.change_all_student_modes(
|
||||
"confused",
|
||||
look_at_arg=self.screen
|
||||
)
|
||||
self.wait(3)
|
||||
|
|
@ -200,6 +200,36 @@ class FourierNameIntro(Scene):
|
|||
self.wait(3)
|
||||
|
||||
|
||||
class ManyCousinsOfFourierThings(Scene):
|
||||
def construct(self):
|
||||
series_variants = VGroup(
|
||||
TextMobject("Complex", "Fourier Series"),
|
||||
TextMobject("Discrete", "Fourier Series"),
|
||||
)
|
||||
transform_variants = VGroup(
|
||||
TextMobject("Discrete", "Fourier Transform"),
|
||||
TextMobject("Fast", "Fourier Transform"),
|
||||
TextMobject("Quantum", "Fourier Transform"),
|
||||
)
|
||||
groups = VGroup(series_variants, transform_variants)
|
||||
for group, vect in zip(groups, [LEFT, RIGHT]):
|
||||
group.scale(0.7)
|
||||
group.arrange(DOWN, aligned_edge=LEFT)
|
||||
group.move_to(
|
||||
vect * FRAME_WIDTH / 4
|
||||
)
|
||||
group.set_color(YELLOW)
|
||||
|
||||
self.play(*[
|
||||
LaggedStartMap(FadeIn, group)
|
||||
for group in groups
|
||||
])
|
||||
self.play(*[
|
||||
LaggedStartMap(FadeOut, group)
|
||||
for group in groups
|
||||
])
|
||||
|
||||
|
||||
class FourierSeriesIllustraiton(Scene):
|
||||
CONFIG = {
|
||||
"n_range": range(1, 31, 2),
|
||||
|
@ -221,32 +251,21 @@ class FourierSeriesIllustraiton(Scene):
|
|||
y_min=-1,
|
||||
y_max=1,
|
||||
)
|
||||
axes2 = axes1.copy()
|
||||
step_func = axes2.get_graph(
|
||||
lambda x: (1 if x < 0.5 else -1),
|
||||
discontinuities=[0.5],
|
||||
color=YELLOW,
|
||||
stroke_width=3,
|
||||
axes1.x_axis.add_numbers(
|
||||
0.5, 1,
|
||||
number_config={"num_decimal_places": 1}
|
||||
)
|
||||
dot = Dot(axes2.c2p(0.5, 0), color=step_func.get_color())
|
||||
dot.scale(0.5)
|
||||
step_func.add(dot)
|
||||
axes2.add(step_func)
|
||||
axes2 = axes1.copy()
|
||||
target_func_graph = self.get_target_func_graph(axes2)
|
||||
axes2.add(target_func_graph)
|
||||
|
||||
arrow = Arrow(LEFT, RIGHT, color=WHITE)
|
||||
VGroup(axes1, arrow, axes2).arrange(RIGHT).shift(UP)
|
||||
|
||||
def generate_nth_func(n):
|
||||
return lambda x: (4 / n / PI) * np.sin(TAU * n * x)
|
||||
|
||||
def generate_kth_partial_sum_func(k):
|
||||
return lambda x: np.sum([
|
||||
generate_nth_func(n)(x)
|
||||
for n in n_range[:k]
|
||||
])
|
||||
group = VGroup(axes1, arrow, axes2)
|
||||
group.arrange(RIGHT, buff=LARGE_BUFF)
|
||||
group.shift(2 * UP)
|
||||
|
||||
sine_graphs = VGroup(*[
|
||||
axes1.get_graph(generate_nth_func(n))
|
||||
axes1.get_graph(self.generate_nth_func(n))
|
||||
for n in n_range
|
||||
])
|
||||
sine_graphs.set_stroke(width=3)
|
||||
|
@ -256,44 +275,43 @@ class FourierSeriesIllustraiton(Scene):
|
|||
)
|
||||
|
||||
partial_sums = VGroup(*[
|
||||
axes1.get_graph(generate_kth_partial_sum_func(k + 1))
|
||||
axes1.get_graph(self.generate_kth_partial_sum_func(k + 1))
|
||||
for k in range(len(n_range))
|
||||
])
|
||||
partial_sums.match_style(sine_graphs)
|
||||
|
||||
sum_tex = TexMobject(
|
||||
"\\frac{4}{\\pi}"
|
||||
"\\sum_{1, 3, 5, \\dots}"
|
||||
"\\frac{1}{n} \\sin(2\\pi \\cdot n \\cdot x)"
|
||||
)
|
||||
sum_tex.next_to(partial_sums, DOWN, buff=0.7)
|
||||
sum_tex = self.get_sum_tex()
|
||||
sum_tex.next_to(axes1, DOWN, LARGE_BUFF)
|
||||
sum_tex.shift(RIGHT)
|
||||
eq = TexMobject("=")
|
||||
step_tex = TexMobject(
|
||||
"""
|
||||
1 \\quad \\text{if $x < 0.5$} \\\\
|
||||
0 \\quad \\text{if $x = 0.5$} \\\\
|
||||
-1 \\quad \\text{if $x > 0.5$} \\\\
|
||||
"""
|
||||
)
|
||||
lb = Brace(step_tex, LEFT, buff=SMALL_BUFF)
|
||||
step_tex.add(lb)
|
||||
step_tex.next_to(axes2, DOWN, buff=MED_LARGE_BUFF)
|
||||
target_func_tex = self.get_target_func_tex()
|
||||
target_func_tex.next_to(axes2, DOWN)
|
||||
target_func_tex.match_y(sum_tex)
|
||||
eq.move_to(midpoint(
|
||||
step_tex.get_left(),
|
||||
target_func_tex.get_left(),
|
||||
sum_tex.get_right()
|
||||
))
|
||||
|
||||
range_words = TextMobject(
|
||||
"For $0 \\le x \\le 1$"
|
||||
)
|
||||
range_words.next_to(
|
||||
VGroup(sum_tex, target_func_tex),
|
||||
DOWN,
|
||||
)
|
||||
|
||||
rects = it.chain(
|
||||
[
|
||||
SurroundingRectangle(sum_tex[0][i])
|
||||
for i in [4, 6, 8]
|
||||
SurroundingRectangle(piece)
|
||||
for piece in self.get_sum_tex_pieces(sum_tex)
|
||||
],
|
||||
it.cycle([None])
|
||||
)
|
||||
|
||||
self.add(axes1, arrow, axes2)
|
||||
self.add(step_func)
|
||||
self.add(sum_tex, eq, step_tex)
|
||||
self.add(target_func_graph)
|
||||
self.add(sum_tex, eq, target_func_tex)
|
||||
self.add(range_words)
|
||||
|
||||
curr_partial_sum = axes1.get_graph(lambda x: 0)
|
||||
curr_partial_sum.set_stroke(width=1)
|
||||
|
@ -320,6 +338,101 @@ class FourierSeriesIllustraiton(Scene):
|
|||
self.play(*anims2)
|
||||
curr_partial_sum = partial_sum
|
||||
|
||||
def get_sum_tex(self):
|
||||
return TexMobject(
|
||||
"\\frac{4}{\\pi} \\left(",
|
||||
"\\frac{\\cos(\\pi x)}{1}",
|
||||
"-\\frac{\\cos(3\\pi x)}{3}",
|
||||
"+\\frac{\\cos(5\\pi x)}{5}",
|
||||
"- \\cdots \\right)"
|
||||
).scale(0.75)
|
||||
|
||||
def get_sum_tex_pieces(self, sum_tex):
|
||||
return sum_tex[1:4]
|
||||
|
||||
def get_target_func_tex(self):
|
||||
step_tex = TexMobject(
|
||||
"""
|
||||
1 \\quad \\text{if $x < 0.5$} \\\\
|
||||
0 \\quad \\text{if $x = 0.5$} \\\\
|
||||
-1 \\quad \\text{if $x > 0.5$} \\\\
|
||||
"""
|
||||
)
|
||||
lb = Brace(step_tex, LEFT, buff=SMALL_BUFF)
|
||||
step_tex.add(lb)
|
||||
return step_tex
|
||||
|
||||
def get_target_func_graph(self, axes):
|
||||
step_func = axes.get_graph(
|
||||
lambda x: (1 if x < 0.5 else -1),
|
||||
discontinuities=[0.5],
|
||||
color=YELLOW,
|
||||
stroke_width=3,
|
||||
)
|
||||
dot = Dot(axes.c2p(0.5, 0), color=step_func.get_color())
|
||||
dot.scale(0.5)
|
||||
step_func.add(dot)
|
||||
return step_func
|
||||
|
||||
# def generate_nth_func(self, n):
|
||||
# return lambda x: (4 / n / PI) * np.sin(TAU * n * x)
|
||||
|
||||
def generate_nth_func(self, n):
|
||||
return lambda x: np.prod([
|
||||
(4 / PI),
|
||||
(1 / n) * (-1)**((n - 1) / 2),
|
||||
np.cos(PI * n * x)
|
||||
])
|
||||
|
||||
def generate_kth_partial_sum_func(self, k):
|
||||
return lambda x: np.sum([
|
||||
self.generate_nth_func(n)(x)
|
||||
for n in self.n_range[:k]
|
||||
])
|
||||
|
||||
|
||||
class FourierSeriesOfLineIllustration(FourierSeriesIllustraiton):
|
||||
CONFIG = {
|
||||
"n_range": range(1, 31, 2)
|
||||
}
|
||||
|
||||
def get_sum_tex(self):
|
||||
return TexMobject(
|
||||
"\\frac{8}{\\pi^2} \\left(",
|
||||
"\\frac{\\cos(\\pi x)}{1^2}",
|
||||
"+\\frac{\\cos(3\\pi x)}{3^2}",
|
||||
"+\\frac{\\cos(5\\pi x)}{5^2}",
|
||||
"+ \\cdots \\right)"
|
||||
).scale(0.75)
|
||||
|
||||
# def get_sum_tex_pieces(self, sum_tex):
|
||||
# return sum_tex[1:4]
|
||||
|
||||
def get_target_func_tex(self):
|
||||
result = TexMobject("1 - 2x")
|
||||
result.scale(1.5)
|
||||
point = VectorizedPoint()
|
||||
point.next_to(result, RIGHT, 1.5 * LARGE_BUFF)
|
||||
# result.add(point)
|
||||
return result
|
||||
|
||||
def get_target_func_graph(self, axes):
|
||||
return axes.get_graph(
|
||||
lambda x: 1 - 2 * x,
|
||||
color=YELLOW,
|
||||
stroke_width=3,
|
||||
)
|
||||
|
||||
# def generate_nth_func(self, n):
|
||||
# return lambda x: (4 / n / PI) * np.sin(TAU * n * x)
|
||||
|
||||
def generate_nth_func(self, n):
|
||||
return lambda x: np.prod([
|
||||
(8 / PI**2),
|
||||
(1 / n**2),
|
||||
np.cos(PI * n * x)
|
||||
])
|
||||
|
||||
|
||||
class CircleAnimationOfF(FourierOfTrebleClef):
|
||||
CONFIG = {
|
||||
|
@ -334,94 +447,568 @@ class CircleAnimationOfF(FourierOfTrebleClef):
|
|||
|
||||
def get_shape(self):
|
||||
path = VMobject()
|
||||
shape = TexMobject("F")
|
||||
shape = TextMobject("F")
|
||||
for sp in shape.family_members_with_points():
|
||||
path.append_points(sp.points)
|
||||
return path
|
||||
|
||||
|
||||
class LastChapterWrapper(Scene):
|
||||
class ExponentialDecay(PiCreatureScene):
|
||||
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)
|
||||
k = 0.2
|
||||
mk_tex = "-0.2"
|
||||
mk_tex_color = GREEN
|
||||
t2c = {mk_tex: mk_tex_color}
|
||||
|
||||
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)
|
||||
# Pi creature
|
||||
randy = self.pi_creature
|
||||
randy.flip()
|
||||
randy.set_height(2.5)
|
||||
randy.move_to(3 * RIGHT)
|
||||
randy.to_edge(DOWN)
|
||||
bubble = ThoughtBubble(
|
||||
direction=RIGHT,
|
||||
height=3,
|
||||
width=4,
|
||||
direction=LEFT,
|
||||
height=3.5,
|
||||
width=3,
|
||||
)
|
||||
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",
|
||||
),
|
||||
bubble.pin_to(randy)
|
||||
bubble.set_fill(DARKER_GREY)
|
||||
exp = TexMobject(
|
||||
"Ce^{", mk_tex, "t}",
|
||||
tex_to_color_map=t2c,
|
||||
)
|
||||
# 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,
|
||||
# )
|
||||
exp.move_to(bubble.get_bubble_center())
|
||||
|
||||
observations.arrange(
|
||||
DOWN,
|
||||
aligned_edge=LEFT,
|
||||
buff=LARGE_BUFF,
|
||||
# Setup axes
|
||||
axes = Axes(
|
||||
x_min=0,
|
||||
x_max=13,
|
||||
y_min=-4,
|
||||
y_max=4,
|
||||
)
|
||||
observations.set_height(FRAME_HEIGHT - 2)
|
||||
observations.to_corner(UL, buff=LARGE_BUFF)
|
||||
axes.set_stroke(width=2)
|
||||
axes.set_color(LIGHT_GREY)
|
||||
axes.scale(0.9)
|
||||
axes.to_edge(LEFT, buff=LARGE_BUFF)
|
||||
axes.x_axis.add_numbers()
|
||||
axes.y_axis.add_numbers()
|
||||
axes.y_axis.add_numbers(0)
|
||||
axes.x_axis.add(
|
||||
TextMobject("Time").next_to(
|
||||
axes.x_axis.get_end(), DR,
|
||||
)
|
||||
)
|
||||
axes.y_axis.add(
|
||||
TexMobject("f").next_to(
|
||||
axes.y_axis.get_corner(UR), RIGHT,
|
||||
).set_color(YELLOW)
|
||||
)
|
||||
axes.x_axis.set_opacity(0)
|
||||
|
||||
self.add(fourier)
|
||||
self.play(ShowCreation(bubble))
|
||||
# Value trackers
|
||||
y_tracker = ValueTracker(3)
|
||||
x_tracker = ValueTracker(0)
|
||||
dydt_tracker = ValueTracker()
|
||||
dxdt_tracker = ValueTracker(0)
|
||||
self.add(
|
||||
y_tracker, x_tracker,
|
||||
dydt_tracker, dxdt_tracker,
|
||||
)
|
||||
|
||||
get_y = y_tracker.get_value
|
||||
get_x = x_tracker.get_value
|
||||
get_dydt = dydt_tracker.get_value
|
||||
get_dxdt = dxdt_tracker.get_value
|
||||
|
||||
dydt_tracker.add_updater(lambda m: m.set_value(
|
||||
- k * get_y()
|
||||
))
|
||||
y_tracker.add_updater(lambda m, dt: m.increment_value(
|
||||
dt * get_dydt()
|
||||
))
|
||||
x_tracker.add_updater(lambda m, dt: m.increment_value(
|
||||
dt * get_dxdt()
|
||||
))
|
||||
|
||||
# Tip/decimal
|
||||
tip = ArrowTip(color=YELLOW)
|
||||
tip.set_width(0.25)
|
||||
tip.add_updater(lambda m: m.move_to(
|
||||
axes.c2p(get_x(), get_y()), LEFT
|
||||
))
|
||||
decimal = DecimalNumber()
|
||||
decimal.add_updater(lambda d: d.set_value(get_y()))
|
||||
decimal.add_updater(lambda d: d.next_to(
|
||||
tip, RIGHT,
|
||||
SMALL_BUFF,
|
||||
))
|
||||
|
||||
# Rate of change arrow
|
||||
arrow = Vector(
|
||||
DOWN, color=RED,
|
||||
max_stroke_width_to_length_ratio=50,
|
||||
max_tip_length_to_length_ratio=0.2,
|
||||
)
|
||||
arrow.set_stroke(width=4)
|
||||
arrow.add_updater(lambda m: m.scale(
|
||||
2.5 * abs(get_dydt()) / m.get_length()
|
||||
))
|
||||
arrow.add_updater(lambda m: m.move_to(
|
||||
tip.get_left(), UP
|
||||
))
|
||||
|
||||
# Graph
|
||||
graph = TracedPath(tip.get_left)
|
||||
|
||||
# Equation
|
||||
ode = TexMobject(
|
||||
"{d{f} \\over dt}(t)",
|
||||
"=", mk_tex, "\\cdot {f}(t)",
|
||||
tex_to_color_map={
|
||||
"{f}": YELLOW,
|
||||
"=": WHITE,
|
||||
mk_tex: mk_tex_color
|
||||
}
|
||||
)
|
||||
ode.to_edge(UP)
|
||||
dfdt = ode[:3]
|
||||
ft = ode[-2:]
|
||||
|
||||
self.add(axes)
|
||||
self.add(tip)
|
||||
self.add(decimal)
|
||||
self.add(arrow)
|
||||
self.add(randy)
|
||||
self.add(ode)
|
||||
|
||||
# Show rate of change dependent on itself
|
||||
rect = SurroundingRectangle(dfdt)
|
||||
self.play(ShowCreation(rect))
|
||||
self.wait()
|
||||
self.play(LaggedStart(*[
|
||||
TransformFromCopy(bubble, observation)
|
||||
for observation in observations
|
||||
], lag_ratio=0.2))
|
||||
self.play(
|
||||
FadeOut(fourier),
|
||||
Transform(
|
||||
rect,
|
||||
SurroundingRectangle(ft)
|
||||
)
|
||||
)
|
||||
self.wait(3)
|
||||
|
||||
# Show graph over time
|
||||
self.play(
|
||||
DrawBorderThenFill(bubble),
|
||||
Write(exp),
|
||||
FadeOut(rect),
|
||||
randy.change, "thinking",
|
||||
)
|
||||
axes.x_axis.set_opacity(1)
|
||||
self.play(
|
||||
y_tracker.set_value, 3,
|
||||
ShowCreation(axes.x_axis),
|
||||
)
|
||||
dxdt_tracker.set_value(1)
|
||||
self.add(graph)
|
||||
randy.add_updater(lambda r: r.look_at(tip))
|
||||
self.wait(4)
|
||||
|
||||
# Show derivative of exponential
|
||||
eq = TexMobject("=")
|
||||
eq.next_to(ode.get_part_by_tex("="), DOWN, LARGE_BUFF)
|
||||
exp.generate_target()
|
||||
exp.target.next_to(eq, LEFT)
|
||||
d_dt = TexMobject("{d \\over dt}")
|
||||
d_dt.next_to(exp.target, LEFT)
|
||||
const = TexMobject(mk_tex)
|
||||
const.set_color(mk_tex_color)
|
||||
dot = TexMobject("\\cdot")
|
||||
const.next_to(eq, RIGHT)
|
||||
dot.next_to(const, RIGHT, 2 * SMALL_BUFF)
|
||||
exp_copy = exp.copy()
|
||||
exp_copy.next_to(dot, RIGHT, 2 * SMALL_BUFF)
|
||||
VGroup(const, dot, eq).align_to(exp_copy, DOWN)
|
||||
|
||||
self.play(
|
||||
MoveToTarget(exp),
|
||||
FadeOut(bubble),
|
||||
FadeIn(d_dt),
|
||||
FadeIn(eq),
|
||||
)
|
||||
self.wait(2)
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
exp[1].copy().replace,
|
||||
const[0],
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
rect = SurroundingRectangle(exp)
|
||||
rect.set_stroke(BLUE, 2)
|
||||
self.play(FadeIn(rect))
|
||||
self.play(
|
||||
Write(dot),
|
||||
TransformFromCopy(exp, exp_copy),
|
||||
rect.move_to, exp_copy
|
||||
)
|
||||
self.play(FadeOut(rect))
|
||||
self.wait(5)
|
||||
|
||||
|
||||
class InvestmentGrowth(Scene):
|
||||
CONFIG = {
|
||||
"output_tex": "{M}",
|
||||
"output_color": GREEN,
|
||||
"initial_value": 1,
|
||||
"initial_value_tex": "{M_0}",
|
||||
"k": 0.05,
|
||||
"k_tex": "0.05",
|
||||
"total_time": 43,
|
||||
"time_rate": 4,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
# Axes
|
||||
axes = Axes(
|
||||
x_min=0,
|
||||
x_max=self.total_time,
|
||||
y_min=0,
|
||||
y_max=6,
|
||||
x_axis_config={
|
||||
"unit_size": 0.3,
|
||||
"tick_size": 0.05,
|
||||
"numbers_with_elongated_ticks": range(
|
||||
0, self.total_time, 5
|
||||
)
|
||||
}
|
||||
)
|
||||
axes.to_corner(DL, buff=LARGE_BUFF)
|
||||
|
||||
time_label = TextMobject("Time")
|
||||
time_label.next_to(
|
||||
axes.x_axis.get_right(),
|
||||
UP, MED_LARGE_BUFF
|
||||
)
|
||||
time_label.shift_onto_screen()
|
||||
axes.x_axis.add(time_label)
|
||||
money_label = TexMobject(self.output_tex)
|
||||
money_label.set_color(self.output_color)
|
||||
money_label.next_to(
|
||||
axes.y_axis.get_top(),
|
||||
UP,
|
||||
)
|
||||
axes.y_axis.add(money_label)
|
||||
|
||||
# Graph
|
||||
graph = axes.get_graph(
|
||||
lambda x: self.initial_value * np.exp(self.k * x)
|
||||
)
|
||||
graph.set_color(self.output_color)
|
||||
full_graph = graph.copy()
|
||||
time_tracker = self.get_time_tracker()
|
||||
graph.add_updater(lambda m: m.pointwise_become_partial(
|
||||
full_graph, 0,
|
||||
np.clip(
|
||||
time_tracker.get_value() / self.total_time,
|
||||
0, 1,
|
||||
)
|
||||
))
|
||||
|
||||
# Equation
|
||||
tex_kwargs = {
|
||||
"tex_to_color_map": {
|
||||
self.output_tex: self.output_color,
|
||||
self.initial_value_tex: BLUE,
|
||||
}
|
||||
}
|
||||
ode = TexMobject(
|
||||
"{d",
|
||||
"\\over dt}",
|
||||
self.output_tex,
|
||||
"(t)",
|
||||
"=", self.k_tex,
|
||||
"\\cdot", self.output_tex, "(t)",
|
||||
**tex_kwargs
|
||||
)
|
||||
ode.to_edge(UP)
|
||||
exp = TexMobject(
|
||||
self.output_tex,
|
||||
"(t) =", self.initial_value_tex,
|
||||
"e^{", self.k_tex, "t}",
|
||||
**tex_kwargs
|
||||
)
|
||||
exp.next_to(ode, DOWN, LARGE_BUFF)
|
||||
|
||||
M0_part = exp.get_part_by_tex(self.initial_value_tex)
|
||||
exp_part = exp[-3:]
|
||||
M0_label = M0_part.copy()
|
||||
M0_label.next_to(
|
||||
axes.c2p(0, self.initial_value),
|
||||
LEFT
|
||||
)
|
||||
M0_part.set_opacity(0)
|
||||
exp_part.save_state()
|
||||
exp_part.align_to(M0_part, LEFT)
|
||||
|
||||
self.add(axes)
|
||||
self.add(graph)
|
||||
self.add(time_tracker)
|
||||
|
||||
self.play(FadeInFromDown(ode))
|
||||
self.wait(2)
|
||||
self.play(FadeInFrom(exp, UP))
|
||||
self.wait(2)
|
||||
self.play(
|
||||
Restore(exp_part),
|
||||
M0_part.set_opacity, 1,
|
||||
)
|
||||
self.play(TransformFromCopy(
|
||||
M0_part, M0_label
|
||||
))
|
||||
self.wait(5)
|
||||
|
||||
def get_time_tracker(self):
|
||||
time_tracker = ValueTracker(0)
|
||||
time_tracker.add_updater(
|
||||
lambda m, dt: m.increment_value(
|
||||
self.time_rate * dt
|
||||
)
|
||||
)
|
||||
return time_tracker
|
||||
|
||||
|
||||
class GrowingPileOfMoney(InvestmentGrowth):
|
||||
CONFIG = {
|
||||
"total_time": 60
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
initial_count = 5
|
||||
k = self.k
|
||||
total_time = self.total_time
|
||||
|
||||
time_tracker = self.get_time_tracker()
|
||||
|
||||
final_count = initial_count * np.exp(k * total_time)
|
||||
dollar_signs = VGroup(*[
|
||||
TexMobject("\\$")
|
||||
for x in range(int(final_count))
|
||||
])
|
||||
dollar_signs.set_color(GREEN)
|
||||
for ds in dollar_signs:
|
||||
ds.shift(
|
||||
3 * np.random.random(3)
|
||||
)
|
||||
dollar_signs.center()
|
||||
dollar_signs.sort(get_norm)
|
||||
dollar_signs.set_stroke(BLACK, 3, background=True)
|
||||
|
||||
def update_dollar_signs(group):
|
||||
t = time_tracker.get_value()
|
||||
count = initial_count * np.exp(k * t)
|
||||
alpha = count / final_count
|
||||
n, sa = integer_interpolate(0, len(dollar_signs), alpha)
|
||||
group.set_opacity(1)
|
||||
group[n:].set_opacity(0)
|
||||
group[n].set_opacity(sa)
|
||||
|
||||
dollar_signs.add_updater(update_dollar_signs)
|
||||
|
||||
self.add(time_tracker)
|
||||
self.add(dollar_signs)
|
||||
self.wait(20)
|
||||
|
||||
|
||||
class CarbonDecayCurve(InvestmentGrowth):
|
||||
CONFIG = {
|
||||
"output_tex": "{^{14}C}",
|
||||
"output_color": GOLD,
|
||||
"initial_value": 4,
|
||||
"initial_value_tex": "{^{14}C_0}",
|
||||
"k": -0.1,
|
||||
"k_tex": "-k",
|
||||
"time_rate": 6,
|
||||
}
|
||||
|
||||
|
||||
class CarbonDecayingInMammoth(Scene):
|
||||
def construct(self):
|
||||
mammoth = SVGMobject("Mammoth")
|
||||
mammoth.set_color(
|
||||
interpolate_color(GREY_BROWN, WHITE, 0.25)
|
||||
)
|
||||
mammoth.set_height(4)
|
||||
body = mammoth[9]
|
||||
|
||||
atoms = VGroup(*[
|
||||
self.get_atom(body)
|
||||
for n in range(600)
|
||||
])
|
||||
|
||||
p_decay = 0.2
|
||||
|
||||
def update_atoms(group, dt):
|
||||
for atom in group:
|
||||
if np.random.random() < dt * p_decay:
|
||||
group.remove(atom)
|
||||
return group
|
||||
atoms.add_updater(update_atoms)
|
||||
|
||||
self.add(mammoth)
|
||||
self.add(atoms)
|
||||
self.wait(20)
|
||||
|
||||
def get_atom(self, body):
|
||||
atom = Dot(color=GOLD)
|
||||
atom.set_height(0.05)
|
||||
|
||||
dl = body.get_corner(DL)
|
||||
ur = body.get_corner(UR)
|
||||
|
||||
wn = 0
|
||||
while wn == 0:
|
||||
point = np.array([
|
||||
interpolate(dl[0], ur[0], np.random.random()),
|
||||
interpolate(dl[1], ur[1], np.random.random()),
|
||||
0
|
||||
])
|
||||
wn = get_winding_number([
|
||||
body.point_from_proportion(a) - point
|
||||
for a in np.linspace(0, 1, 300)
|
||||
])
|
||||
wn = int(np.round(wn))
|
||||
|
||||
atom.move_to(point)
|
||||
return atom
|
||||
|
||||
|
||||
class BoundaryConditionInterlude(Scene):
|
||||
def construct(self):
|
||||
background = FullScreenFadeRectangle(
|
||||
fill_color=DARK_GREY
|
||||
)
|
||||
storyline = self.get_main_storyline()
|
||||
storyline.generate_target()
|
||||
v_shift = 2 * DOWN
|
||||
storyline.target.shift(v_shift)
|
||||
im_to_im = storyline[1].get_center() - storyline[0].get_center()
|
||||
|
||||
bc_image = self.get_labeled_image(
|
||||
"Boundary\\\\conditions",
|
||||
"boundary_condition_thumbnail"
|
||||
)
|
||||
bc_image.next_to(storyline[1], UP)
|
||||
new_arrow0 = Arrow(
|
||||
storyline.arrows[0].get_start() + v_shift,
|
||||
bc_image.get_left() + SMALL_BUFF * LEFT,
|
||||
path_arc=-90 * DEGREES,
|
||||
buff=0,
|
||||
)
|
||||
new_mid_arrow = Arrow(
|
||||
bc_image.get_bottom(),
|
||||
storyline[1].get_top() + v_shift,
|
||||
buff=SMALL_BUFF,
|
||||
path_arc=60 * DEGREES,
|
||||
)
|
||||
|
||||
self.add(background)
|
||||
self.add(storyline[0])
|
||||
for im1, im2, arrow in zip(storyline, storyline[1:], storyline.arrows):
|
||||
self.add(im2, im1)
|
||||
self.play(
|
||||
FadeInFrom(im2, -im_to_im),
|
||||
ShowCreation(arrow),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
GrowFromCenter(bc_image),
|
||||
MoveToTarget(storyline),
|
||||
Transform(
|
||||
storyline.arrows[0],
|
||||
new_arrow0,
|
||||
),
|
||||
MaintainPositionRelativeTo(
|
||||
storyline.arrows[1],
|
||||
storyline,
|
||||
),
|
||||
)
|
||||
self.play(ShowCreation(new_mid_arrow))
|
||||
self.wait()
|
||||
|
||||
def get_main_storyline(self):
|
||||
images = Group(
|
||||
self.get_sine_curve_image(),
|
||||
self.get_linearity_image(),
|
||||
self.get_fourier_series_image(),
|
||||
)
|
||||
for image in images:
|
||||
image.set_height(3)
|
||||
images.arrange(RIGHT, buff=1)
|
||||
images.set_width(FRAME_WIDTH - 1)
|
||||
|
||||
arrows = VGroup()
|
||||
for im1, im2 in zip(images, images[1:]):
|
||||
arrow = Arrow(
|
||||
im1.get_top(),
|
||||
im2.get_top(),
|
||||
color=WHITE,
|
||||
buff=MED_SMALL_BUFF,
|
||||
path_arc=-120 * DEGREES,
|
||||
rectangular_stem_width=0.025,
|
||||
)
|
||||
arrow.scale(0.7, about_edge=DOWN)
|
||||
arrows.add(arrow)
|
||||
images.arrows = arrows
|
||||
|
||||
return images
|
||||
|
||||
def get_sine_curve_image(self):
|
||||
return self.get_labeled_image(
|
||||
"Sine curves",
|
||||
"sine_curve_temp_graph",
|
||||
)
|
||||
|
||||
def get_linearity_image(self):
|
||||
return self.get_labeled_image(
|
||||
"Linearity",
|
||||
"linearity_thumbnail",
|
||||
)
|
||||
|
||||
def get_fourier_series_image(self):
|
||||
return self.get_labeled_image(
|
||||
"Fourier series",
|
||||
"fourier_series_thumbnail",
|
||||
)
|
||||
|
||||
def get_labeled_image(self, text, image_file):
|
||||
rect = ScreenRectangle(height=2)
|
||||
border = rect.copy()
|
||||
rect.set_fill(BLACK, 1)
|
||||
rect.set_stroke(WHITE, 0)
|
||||
border.set_stroke(WHITE, 2)
|
||||
|
||||
text_mob = TextMobject(text)
|
||||
text_mob.set_stroke(BLACK, 5, background=True)
|
||||
text_mob.next_to(rect.get_top(), DOWN, SMALL_BUFF)
|
||||
|
||||
image = ImageMobject(image_file)
|
||||
image.replace(rect, dim_to_match=1)
|
||||
image.scale(0.8, about_edge=DOWN)
|
||||
|
||||
return Group(rect, image, border, text_mob)
|
||||
|
||||
|
||||
class GiantCross(Scene):
|
||||
def construct(self):
|
||||
rect = FullScreenFadeRectangle()
|
||||
cross = Cross(rect)
|
||||
cross.set_stroke(RED, 25)
|
||||
|
||||
words = TextMobject("This wouldn't\\\\happen!")
|
||||
words.scale(2)
|
||||
words.set_color(RED)
|
||||
words.to_edge(UP)
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(words),
|
||||
ShowCreation(cross),
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class NewSceneName(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
|
|
File diff suppressed because it is too large
Load diff
465
active_projects/ode/part3/wordy_scenes.py
Normal file
465
active_projects/ode/part3/wordy_scenes.py
Normal file
|
@ -0,0 +1,465 @@
|
|||
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)
|
||||
|
||||
|
||||
class EquationAboveSineAnalysis(WriteHeatEquationTemplate):
|
||||
def construct(self):
|
||||
equation = self.get_d1_equation()
|
||||
equation.to_edge(UP)
|
||||
equation.shift(2 * LEFT)
|
||||
eq_index = equation.index_of_part_by_tex("=")
|
||||
lhs = equation[:eq_index]
|
||||
eq = equation[eq_index]
|
||||
rhs = equation[eq_index + 1:]
|
||||
t_terms = equation.get_parts_by_tex("{t}")[1:]
|
||||
t_terms.save_state()
|
||||
zeros = VGroup(*[
|
||||
TexMobject("0").replace(t, dim_to_match=1)
|
||||
for t in t_terms
|
||||
])
|
||||
zeros.align_to(t_terms, DOWN)
|
||||
new_rhs = TexMobject(
|
||||
"=", "-\\alpha \\cdot {T}", "({x}, 0)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
# new_rhs.move_to(equation.get_right())
|
||||
# new_rhs.next_to(equation, DOWN, MED_LARGE_BUFF)
|
||||
# new_rhs.align_to(eq, LEFT)
|
||||
new_rhs.next_to(equation, RIGHT)
|
||||
new_rhs.shift(SMALL_BUFF * DOWN)
|
||||
|
||||
self.add(equation)
|
||||
self.play(ShowCreationThenFadeAround(rhs))
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeOutAndShift(t_terms, UP),
|
||||
FadeInFrom(zeros, DOWN),
|
||||
)
|
||||
t_terms.fade(1)
|
||||
self.wait()
|
||||
self.play(
|
||||
# VGroup(equation, zeros).next_to,
|
||||
# new_rhs, LEFT,
|
||||
FadeIn(new_rhs),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
VGroup(
|
||||
lhs[6:],
|
||||
eq,
|
||||
rhs,
|
||||
new_rhs[0],
|
||||
new_rhs[-3:],
|
||||
zeros,
|
||||
).fade, 0.5,
|
||||
)
|
||||
self.play(ShowCreationThenFadeAround(lhs[:6]))
|
||||
self.play(ShowCreationThenFadeAround(new_rhs[1:-3]))
|
||||
self.wait()
|
||||
|
||||
|
||||
class ShowSinExpDerivatives(WriteHeatEquationTemplate):
|
||||
CONFIG = {
|
||||
"tex_mobject_config": {
|
||||
"tex_to_color_map": {
|
||||
"{0}": WHITE,
|
||||
"\\partial": WHITE,
|
||||
"=": WHITE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
pde = self.get_d1_equation_without_inputs()
|
||||
pde.to_edge(UP)
|
||||
pde.generate_target()
|
||||
|
||||
new_rhs = TexMobject(
|
||||
"=- \\alpha \\cdot T",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
new_rhs.next_to(pde, RIGHT)
|
||||
new_rhs.align_to(pde.get_part_by_tex("alpha"), DOWN)
|
||||
|
||||
equation1 = TexMobject(
|
||||
"T({x}, {0}) = \\sin\\left({x}\\right)",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
equation2 = TexMobject(
|
||||
"T({x}, {t}) = \\sin\\left({x}\\right)",
|
||||
"e^{-\\alpha{t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
for eq in equation1, equation2:
|
||||
eq.next_to(pde, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
eq2_part1 = equation2[:len(equation1)]
|
||||
eq2_part2 = equation2[len(equation1):]
|
||||
|
||||
# Rectangles
|
||||
exp_rect = SurroundingRectangle(eq2_part2)
|
||||
exp_rect.set_stroke(RED, 3)
|
||||
sin_rect = SurroundingRectangle(
|
||||
eq2_part1[-3:]
|
||||
)
|
||||
sin_rect.set_color(BLUE)
|
||||
|
||||
VGroup(pde.target, new_rhs).center().to_edge(UP)
|
||||
|
||||
# Show proposed solution
|
||||
self.add(pde)
|
||||
self.add(equation1)
|
||||
self.wait()
|
||||
self.play(
|
||||
MoveToTarget(pde),
|
||||
FadeInFrom(new_rhs, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
ReplacementTransform(equation1, eq2_part1),
|
||||
FadeIn(eq2_part2),
|
||||
)
|
||||
self.play(ShowCreation(exp_rect))
|
||||
self.wait()
|
||||
self.play(FadeOut(exp_rect))
|
||||
|
||||
# Take partial derivatives wrt x
|
||||
q_mark = TexMobject("?")
|
||||
q_mark.next_to(pde.get_part_by_tex("="), UP)
|
||||
q_mark.set_color(RED)
|
||||
|
||||
arrow1 = Vector(3 * DOWN + 1 * RIGHT, color=WHITE)
|
||||
arrow1.scale(1.2 / arrow1.get_length())
|
||||
arrow1.next_to(
|
||||
eq2_part2.get_corner(DL),
|
||||
DOWN, MED_LARGE_BUFF
|
||||
)
|
||||
ddx_label1 = TexMobject(
|
||||
"\\partial \\over \\partial {x}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
ddx_label1.scale(0.7)
|
||||
ddx_label1.next_to(
|
||||
arrow1.point_from_proportion(0.8),
|
||||
UR, SMALL_BUFF
|
||||
)
|
||||
|
||||
pde_ddx = VGroup(
|
||||
*pde.get_parts_by_tex("\\partial")[2:],
|
||||
pde.get_parts_by_tex("\\over")[1],
|
||||
pde.get_part_by_tex("{x}"),
|
||||
)
|
||||
pde_ddx_rect = SurroundingRectangle(pde_ddx)
|
||||
pde_ddx_rect.set_color(GREEN)
|
||||
|
||||
eq2_part2_rect = SurroundingRectangle(eq2_part2)
|
||||
|
||||
dx = TexMobject(
|
||||
"\\cos\\left({x}\\right)", "e^{-\\alpha {t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
ddx = TexMobject(
|
||||
"-\\sin\\left({x}\\right)", "e^{-\\alpha {t}}",
|
||||
**self.tex_mobject_config
|
||||
)
|
||||
dx.next_to(arrow1, DOWN)
|
||||
dx.align_to(eq2_part2, RIGHT)
|
||||
x_shift = arrow1.get_end()[0] - arrow1.get_start()[0]
|
||||
x_shift *= 2
|
||||
dx.shift(x_shift * RIGHT)
|
||||
arrow2 = arrow1.copy()
|
||||
arrow2.next_to(dx, DOWN)
|
||||
arrow2.shift(MED_SMALL_BUFF * RIGHT)
|
||||
dx_arrows = VGroup(arrow1, arrow2)
|
||||
|
||||
ddx_label2 = ddx_label1.copy()
|
||||
ddx_label2.shift(
|
||||
arrow2.get_center() - arrow1.get_center()
|
||||
)
|
||||
ddx.next_to(arrow2, DOWN)
|
||||
ddx.align_to(eq2_part2, RIGHT)
|
||||
ddx.shift(2 * x_shift * RIGHT)
|
||||
|
||||
rhs = equation2[-6:]
|
||||
|
||||
self.play(
|
||||
FadeInFromDown(q_mark)
|
||||
)
|
||||
self.play(
|
||||
ShowCreation(pde_ddx_rect)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStart(
|
||||
GrowArrow(arrow1),
|
||||
GrowArrow(arrow2),
|
||||
),
|
||||
TransformFromCopy(
|
||||
pde_ddx[0], ddx_label1
|
||||
),
|
||||
TransformFromCopy(
|
||||
pde_ddx[0], ddx_label2
|
||||
),
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(rhs, dx)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
FadeIn(eq2_part2_rect)
|
||||
)
|
||||
self.play(
|
||||
Transform(
|
||||
eq2_part2_rect,
|
||||
SurroundingRectangle(dx[-3:])
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
FadeOut(eq2_part2_rect)
|
||||
)
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(dx, ddx)
|
||||
)
|
||||
self.play(
|
||||
FadeIn(
|
||||
SurroundingRectangle(ddx).match_style(
|
||||
pde_ddx_rect
|
||||
)
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
# Take partial derivative wrt t
|
||||
pde_ddt = pde[:pde.index_of_part_by_tex("=") - 1]
|
||||
pde_ddt_rect = SurroundingRectangle(pde_ddt)
|
||||
|
||||
dt_arrow = Arrow(
|
||||
arrow1.get_start(),
|
||||
arrow2.get_end() + RIGHT,
|
||||
buff=0
|
||||
)
|
||||
dt_arrow.flip(UP)
|
||||
dt_arrow.next_to(dx_arrows, LEFT, MED_LARGE_BUFF)
|
||||
|
||||
dt_label = TexMobject(
|
||||
"\\partial \\over \\partial {t}",
|
||||
**self.tex_mobject_config,
|
||||
)
|
||||
dt_label.scale(1)
|
||||
dt_label.next_to(
|
||||
dt_arrow.get_center(), UL,
|
||||
SMALL_BUFF,
|
||||
)
|
||||
|
||||
rhs_copy = rhs.copy()
|
||||
rhs_copy.next_to(dt_arrow.get_end(), DOWN)
|
||||
rhs_copy.shift(MED_LARGE_BUFF * LEFT)
|
||||
rhs_copy.match_y(ddx)
|
||||
|
||||
minus_alpha_in_exp = rhs_copy[-3][1:].copy()
|
||||
minus_alpha_in_exp.set_color(RED)
|
||||
minus_alpha = TexMobject("-\\alpha")
|
||||
minus_alpha.next_to(rhs_copy, LEFT)
|
||||
minus_alpha.align_to(rhs_copy[0][0], DOWN)
|
||||
dot = TexMobject("\\cdot")
|
||||
dot.move_to(midpoint(
|
||||
minus_alpha.get_right(),
|
||||
rhs_copy.get_left(),
|
||||
))
|
||||
|
||||
self.play(
|
||||
TransformFromCopy(
|
||||
pde_ddx_rect,
|
||||
pde_ddt_rect,
|
||||
)
|
||||
)
|
||||
self.play(
|
||||
GrowArrow(dt_arrow),
|
||||
TransformFromCopy(
|
||||
pde_ddt,
|
||||
dt_label,
|
||||
)
|
||||
)
|
||||
self.wait()
|
||||
self.play(TransformFromCopy(rhs, rhs_copy))
|
||||
self.play(FadeIn(minus_alpha_in_exp))
|
||||
self.play(
|
||||
ApplyMethod(
|
||||
minus_alpha_in_exp.replace, minus_alpha,
|
||||
path_arc=TAU / 4
|
||||
),
|
||||
FadeIn(dot),
|
||||
)
|
||||
self.play(
|
||||
FadeIn(minus_alpha),
|
||||
FadeOut(minus_alpha_in_exp),
|
||||
)
|
||||
self.wait()
|
||||
rhs_copy.add(minus_alpha, dot)
|
||||
self.play(
|
||||
FadeIn(SurroundingRectangle(rhs_copy))
|
||||
)
|
||||
self.wait()
|
||||
|
||||
#
|
||||
checkmark = TexMobject("\\checkmark")
|
||||
checkmark.set_color(GREEN)
|
||||
checkmark.move_to(q_mark, DOWN)
|
||||
self.play(
|
||||
FadeInFromDown(checkmark),
|
||||
FadeOutAndShift(q_mark, UP)
|
||||
)
|
||||
self.wait()
|
|
@ -6,6 +6,8 @@ from manimlib.utils.rate_functions import linear
|
|||
from manimlib.utils.rate_functions import double_smooth
|
||||
from manimlib.utils.rate_functions import smooth
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class ShowPartial(Animation):
|
||||
"""
|
||||
|
@ -120,6 +122,7 @@ class Write(DrawBorderThenFill):
|
|||
class ShowIncreasingSubsets(Animation):
|
||||
CONFIG = {
|
||||
"suspend_mobject_updating": False,
|
||||
"int_func": np.floor,
|
||||
}
|
||||
|
||||
def __init__(self, group, **kwargs):
|
||||
|
@ -128,5 +131,5 @@ class ShowIncreasingSubsets(Animation):
|
|||
|
||||
def interpolate_mobject(self, alpha):
|
||||
n_submobs = len(self.all_submobs)
|
||||
index = int(alpha * n_submobs)
|
||||
index = int(self.int_func(alpha * n_submobs))
|
||||
self.mobject.submobjects = self.all_submobs[:index]
|
||||
|
|
|
@ -332,6 +332,8 @@ class Camera(object):
|
|||
points = self.transform_points_pre_display(
|
||||
vmobject, vmobject.points
|
||||
)
|
||||
# TODO, shouldn't this be handled in transform_points_pre_display?
|
||||
points = points - self.get_frame_center()
|
||||
if len(points) == 0:
|
||||
return
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from manimlib.constants import *
|
||||
from manimlib.mobject.types.vectorized_mobject import VMobject
|
||||
from manimlib.mobject.types.vectorized_mobject import VGroup
|
||||
from manimlib.utils.rate_functions import smooth
|
||||
from manimlib.utils.space_ops import get_norm
|
||||
|
||||
|
||||
class AnimatedBoundary(VGroup):
|
||||
|
@ -66,3 +68,31 @@ class AnimatedBoundary(VGroup):
|
|||
for sm1, sm2 in zip(family1, family2):
|
||||
sm1.pointwise_become_partial(sm2, a, b)
|
||||
return self
|
||||
|
||||
|
||||
class TracedPath(VMobject):
|
||||
CONFIG = {
|
||||
"stroke_width": 2,
|
||||
"stroke_color": WHITE,
|
||||
"min_distance_to_new_point": 0.1,
|
||||
}
|
||||
|
||||
def __init__(self, traced_point_func, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.traced_point_func = traced_point_func
|
||||
self.add_updater(lambda m: m.update_path())
|
||||
|
||||
def update_path(self):
|
||||
new_point = self.traced_point_func()
|
||||
if self.has_no_points():
|
||||
self.start_new_path(new_point)
|
||||
self.add_line_to(new_point)
|
||||
else:
|
||||
# Set the end to be the new point
|
||||
self.points[-1] = new_point
|
||||
|
||||
# Second to last point
|
||||
nppcc = self.n_points_per_cubic_curve
|
||||
dist = get_norm(new_point - self.points[-nppcc])
|
||||
if dist >= self.min_distance_to_new_point:
|
||||
self.add_line_to(new_point)
|
||||
|
|
|
@ -242,6 +242,7 @@ class VMobject(Mobject):
|
|||
def set_opacity(self, opacity, family=True):
|
||||
self.set_fill(opacity=opacity, family=family)
|
||||
self.set_stroke(opacity=opacity, family=family)
|
||||
self.set_stroke(opacity=opacity, family=family, background=True)
|
||||
return self
|
||||
|
||||
def fade(self, darkness=0.5, family=True):
|
||||
|
|
|
@ -43,7 +43,7 @@ def stage_scenes(module_name):
|
|||
# }
|
||||
# TODO, fix this
|
||||
animation_dir = os.path.join(
|
||||
consts.VIDEO_DIR, "ode", "part2", "1440p60"
|
||||
consts.VIDEO_DIR, "ode", "part3", "1440p60"
|
||||
)
|
||||
#
|
||||
files = os.listdir(animation_dir)
|
||||
|
|
Loading…
Add table
Reference in a new issue