mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
b7fcc68b55
7 changed files with 713 additions and 2 deletions
13
active_projects/ode/all_part3_scenes.py
Normal file
13
active_projects/ode/all_part3_scenes.py
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
from active_projects.ode.part3.staging import *
|
||||||
|
from active_projects.ode.part3.temperature_graphs import *
|
||||||
|
|
||||||
|
|
||||||
|
OUTPUT_DIRECTORY = "ode/part3"
|
||||||
|
SCENES_IN_ORDER = [
|
||||||
|
FourierSeriesIllustraiton,
|
||||||
|
FourierNameIntro,
|
||||||
|
CircleAnimationOfF,
|
||||||
|
LastChapterWrapper,
|
||||||
|
ThreeMainObservations,
|
||||||
|
SimpleSinExpGraph,
|
||||||
|
]
|
|
@ -527,7 +527,7 @@ class FourierNDQ(FourierOfTrebleClef):
|
||||||
|
|
||||||
def get_shape(self):
|
def get_shape(self):
|
||||||
path = VMobject()
|
path = VMobject()
|
||||||
shape = TexMobject("Hayley")
|
shape = TexMobject("NDQ")
|
||||||
for sp in shape.family_members_with_points():
|
for sp in shape.family_members_with_points():
|
||||||
path.append_points(sp.points)
|
path.append_points(sp.points)
|
||||||
return path
|
return path
|
||||||
|
|
427
active_projects/ode/part3/staging.py
Normal file
427
active_projects/ode/part3/staging.py
Normal file
|
@ -0,0 +1,427 @@
|
||||||
|
from manimlib.imports import *
|
||||||
|
|
||||||
|
from active_projects.ode.part2.fourier_series import FourierOfTrebleClef
|
||||||
|
|
||||||
|
|
||||||
|
class FourierNameIntro(Scene):
|
||||||
|
def construct(self):
|
||||||
|
self.show_two_titles()
|
||||||
|
self.transition_to_image()
|
||||||
|
self.show_paper()
|
||||||
|
|
||||||
|
def show_two_titles(self):
|
||||||
|
lt = TextMobject("Fourier", "Series")
|
||||||
|
rt = TextMobject("Fourier", "Transform")
|
||||||
|
lt_variants = VGroup(
|
||||||
|
TextMobject("Complex", "Fourier Series"),
|
||||||
|
TextMobject("Discrete", "Fourier Series"),
|
||||||
|
)
|
||||||
|
rt_variants = VGroup(
|
||||||
|
TextMobject("Discrete", "Fourier Transform"),
|
||||||
|
TextMobject("Fast", "Fourier Transform"),
|
||||||
|
TextMobject("Quantum", "Fourier Transform"),
|
||||||
|
)
|
||||||
|
|
||||||
|
titles = VGroup(lt, rt)
|
||||||
|
titles.scale(1.5)
|
||||||
|
for title, vect in (lt, LEFT), (rt, RIGHT):
|
||||||
|
title.move_to(vect * FRAME_WIDTH / 4)
|
||||||
|
title.to_edge(UP)
|
||||||
|
|
||||||
|
for title, variants in (lt, lt_variants), (rt, rt_variants):
|
||||||
|
title.save_state()
|
||||||
|
title.target = title.copy()
|
||||||
|
title.target.scale(1 / 1.5, about_edge=RIGHT)
|
||||||
|
for variant in variants:
|
||||||
|
variant.move_to(title.target, UR)
|
||||||
|
variant[0].set_color(YELLOW)
|
||||||
|
|
||||||
|
v_line = Line(UP, DOWN)
|
||||||
|
v_line.set_height(FRAME_HEIGHT)
|
||||||
|
v_line.set_stroke(WHITE, 2)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(lt, RIGHT),
|
||||||
|
ShowCreation(v_line)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(rt, LEFT),
|
||||||
|
)
|
||||||
|
# Edit in images of circle animations
|
||||||
|
# and clips from FT video
|
||||||
|
|
||||||
|
# for title, variants in (rt, rt_variants), (lt, lt_variants):
|
||||||
|
for title, variants in [(rt, rt_variants)]:
|
||||||
|
# Maybe do it for left variant, maybe not...
|
||||||
|
self.play(
|
||||||
|
MoveToTarget(title),
|
||||||
|
FadeInFrom(variants[0][0], LEFT)
|
||||||
|
)
|
||||||
|
for v1, v2 in zip(variants, variants[1:]):
|
||||||
|
self.play(
|
||||||
|
FadeOutAndShift(v1[0], UP),
|
||||||
|
FadeInFrom(v2[0], DOWN),
|
||||||
|
run_time=0.5,
|
||||||
|
)
|
||||||
|
self.wait(0.5)
|
||||||
|
self.play(
|
||||||
|
Restore(title),
|
||||||
|
FadeOut(variants[-1][0])
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.titles = titles
|
||||||
|
self.v_line = v_line
|
||||||
|
|
||||||
|
def transition_to_image(self):
|
||||||
|
titles = self.titles
|
||||||
|
v_line = self.v_line
|
||||||
|
|
||||||
|
image = ImageMobject("Joseph Fourier")
|
||||||
|
image.set_height(5)
|
||||||
|
image.to_edge(LEFT)
|
||||||
|
|
||||||
|
frame = Rectangle()
|
||||||
|
frame.replace(image, stretch=True)
|
||||||
|
|
||||||
|
name = TextMobject("Joseph", "Fourier")
|
||||||
|
fourier_part = name.get_part_by_tex("Fourier")
|
||||||
|
fourier_part.set_color(YELLOW)
|
||||||
|
F_sym = fourier_part[0]
|
||||||
|
name.match_width(image)
|
||||||
|
name.next_to(image, DOWN)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(v_line, frame),
|
||||||
|
FadeIn(image),
|
||||||
|
FadeIn(name[0]),
|
||||||
|
*[
|
||||||
|
ReplacementTransform(
|
||||||
|
title[0].deepcopy(),
|
||||||
|
name[1]
|
||||||
|
)
|
||||||
|
for title in titles
|
||||||
|
],
|
||||||
|
titles.scale, 0.65,
|
||||||
|
titles.arrange, DOWN,
|
||||||
|
titles.next_to, image, UP,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
big_F = F_sym.copy()
|
||||||
|
big_F.set_fill(opacity=0)
|
||||||
|
big_F.set_stroke(WHITE, 2)
|
||||||
|
big_F.set_height(3)
|
||||||
|
big_F.move_to(midpoint(
|
||||||
|
image.get_right(),
|
||||||
|
RIGHT_SIDE,
|
||||||
|
))
|
||||||
|
big_F.shift(DOWN)
|
||||||
|
equivalence = VGroup(
|
||||||
|
fourier_part.copy().scale(1.25),
|
||||||
|
TexMobject("\\Leftrightarrow").scale(1.5),
|
||||||
|
TextMobject("Break down into\\\\pure frequencies"),
|
||||||
|
)
|
||||||
|
equivalence.arrange(RIGHT)
|
||||||
|
equivalence.move_to(big_F)
|
||||||
|
equivalence.to_edge(UP)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FadeIn(big_F),
|
||||||
|
TransformFromCopy(fourier_part, equivalence[0]),
|
||||||
|
Write(equivalence[1:]),
|
||||||
|
)
|
||||||
|
self.wait(3)
|
||||||
|
self.play(FadeOut(VGroup(big_F, equivalence)))
|
||||||
|
|
||||||
|
self.image = image
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def show_paper(self):
|
||||||
|
image = self.image
|
||||||
|
paper = ImageMobject("Fourier paper")
|
||||||
|
paper.match_height(image)
|
||||||
|
paper.next_to(image, RIGHT, MED_LARGE_BUFF)
|
||||||
|
|
||||||
|
date = TexMobject("1822")
|
||||||
|
date.next_to(paper, DOWN)
|
||||||
|
date_rect = SurroundingRectangle(date)
|
||||||
|
date_rect.scale(0.3)
|
||||||
|
date_rect.set_color(RED)
|
||||||
|
date_rect.shift(1.37 * UP + 0.08 * LEFT)
|
||||||
|
date_arrow = Arrow(
|
||||||
|
date_rect.get_bottom(),
|
||||||
|
date.get_top(),
|
||||||
|
buff=SMALL_BUFF,
|
||||||
|
color=date_rect.get_color(),
|
||||||
|
)
|
||||||
|
|
||||||
|
heat_rect = SurroundingRectangle(
|
||||||
|
TextMobject("CHALEUR")
|
||||||
|
)
|
||||||
|
heat_rect.set_color(RED)
|
||||||
|
heat_rect.scale(0.6)
|
||||||
|
heat_rect.move_to(
|
||||||
|
paper.get_top() +
|
||||||
|
1.22 * DOWN + 0.37 * RIGHT
|
||||||
|
)
|
||||||
|
heat_word = TextMobject("Heat")
|
||||||
|
heat_word.scale(1.5)
|
||||||
|
heat_word.next_to(paper, UP)
|
||||||
|
heat_word.shift(paper.get_width() * RIGHT)
|
||||||
|
heat_arrow = Arrow(
|
||||||
|
heat_rect.get_top(),
|
||||||
|
heat_word.get_left(),
|
||||||
|
buff=0.1,
|
||||||
|
path_arc=-60 * DEGREES,
|
||||||
|
color=heat_rect.get_color(),
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(FadeInFrom(paper, LEFT))
|
||||||
|
self.play(
|
||||||
|
ShowCreation(date_rect),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
GrowFromPoint(date, date_arrow.get_start()),
|
||||||
|
ShowCreation(date_arrow),
|
||||||
|
)
|
||||||
|
self.wait(3)
|
||||||
|
|
||||||
|
# Insert animation of circles/sine waves
|
||||||
|
# approximating a square wave
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ShowCreation(heat_rect),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
GrowFromPoint(heat_word, heat_arrow.get_start()),
|
||||||
|
ShowCreation(heat_arrow),
|
||||||
|
)
|
||||||
|
self.wait(3)
|
||||||
|
|
||||||
|
|
||||||
|
class FourierSeriesIllustraiton(Scene):
|
||||||
|
CONFIG = {
|
||||||
|
"n_range": range(1, 31, 2),
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
n_range = self.n_range
|
||||||
|
|
||||||
|
axes1 = Axes(
|
||||||
|
number_line_config={
|
||||||
|
"include_tip": False,
|
||||||
|
},
|
||||||
|
x_axis_config={
|
||||||
|
"tick_frequency": 1 / 4,
|
||||||
|
"unit_size": 4,
|
||||||
|
},
|
||||||
|
x_min=0,
|
||||||
|
x_max=1,
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
dot = Dot(axes2.c2p(0.5, 0), color=step_func.get_color())
|
||||||
|
dot.scale(0.5)
|
||||||
|
step_func.add(dot)
|
||||||
|
axes2.add(step_func)
|
||||||
|
|
||||||
|
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]
|
||||||
|
])
|
||||||
|
|
||||||
|
sine_graphs = VGroup(*[
|
||||||
|
axes1.get_graph(generate_nth_func(n))
|
||||||
|
for n in n_range
|
||||||
|
])
|
||||||
|
sine_graphs.set_stroke(width=3)
|
||||||
|
sine_graphs.set_color_by_gradient(
|
||||||
|
BLUE, GREEN, RED, YELLOW, PINK,
|
||||||
|
BLUE, GREEN, RED, YELLOW, PINK,
|
||||||
|
)
|
||||||
|
|
||||||
|
partial_sums = VGroup(*[
|
||||||
|
axes1.get_graph(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)
|
||||||
|
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)
|
||||||
|
eq.move_to(midpoint(
|
||||||
|
step_tex.get_left(),
|
||||||
|
sum_tex.get_right()
|
||||||
|
))
|
||||||
|
|
||||||
|
rects = it.chain(
|
||||||
|
[
|
||||||
|
SurroundingRectangle(sum_tex[0][i])
|
||||||
|
for i in [4, 6, 8]
|
||||||
|
],
|
||||||
|
it.cycle([None])
|
||||||
|
)
|
||||||
|
|
||||||
|
self.add(axes1, arrow, axes2)
|
||||||
|
self.add(step_func)
|
||||||
|
self.add(sum_tex, eq, step_tex)
|
||||||
|
|
||||||
|
curr_partial_sum = axes1.get_graph(lambda x: 0)
|
||||||
|
curr_partial_sum.set_stroke(width=1)
|
||||||
|
for sine_graph, partial_sum, rect in zip(sine_graphs, partial_sums, rects):
|
||||||
|
anims1 = [
|
||||||
|
ShowCreation(sine_graph)
|
||||||
|
]
|
||||||
|
partial_sum.set_stroke(BLACK, 4, background=True)
|
||||||
|
anims2 = [
|
||||||
|
curr_partial_sum.set_stroke,
|
||||||
|
{"width": 1, "opacity": 0.5},
|
||||||
|
curr_partial_sum.set_stroke,
|
||||||
|
{"width": 0, "background": True},
|
||||||
|
ReplacementTransform(
|
||||||
|
sine_graph, partial_sum,
|
||||||
|
remover=True
|
||||||
|
),
|
||||||
|
]
|
||||||
|
if rect:
|
||||||
|
rect.match_style(sine_graph)
|
||||||
|
anims1.append(ShowCreation(rect))
|
||||||
|
anims2.append(FadeOut(rect))
|
||||||
|
self.play(*anims1)
|
||||||
|
self.play(*anims2)
|
||||||
|
curr_partial_sum = partial_sum
|
||||||
|
|
||||||
|
|
||||||
|
class CircleAnimationOfF(FourierOfTrebleClef):
|
||||||
|
CONFIG = {
|
||||||
|
"height": 3,
|
||||||
|
"n_circles": 200,
|
||||||
|
"run_time": 10,
|
||||||
|
"arrow_config": {
|
||||||
|
"tip_length": 0.1,
|
||||||
|
"stroke_width": 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_shape(self):
|
||||||
|
path = VMobject()
|
||||||
|
shape = TexMobject("F")
|
||||||
|
for sp in shape.family_members_with_points():
|
||||||
|
path.append_points(sp.points)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
class LastChapterWrapper(Scene):
|
||||||
|
def construct(self):
|
||||||
|
full_rect = FullScreenFadeRectangle(
|
||||||
|
fill_color=DARK_GREY,
|
||||||
|
fill_opacity=1,
|
||||||
|
)
|
||||||
|
rect = ScreenRectangle(height=6)
|
||||||
|
rect.set_stroke(WHITE, 2)
|
||||||
|
rect.set_fill(BLACK, 1)
|
||||||
|
title = TextMobject("Last chapter")
|
||||||
|
title.scale(2)
|
||||||
|
title.to_edge(UP)
|
||||||
|
rect.next_to(title, DOWN)
|
||||||
|
|
||||||
|
self.add(full_rect)
|
||||||
|
self.play(
|
||||||
|
FadeIn(rect),
|
||||||
|
Write(title, run_time=2),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class ThreeMainObservations(Scene):
|
||||||
|
def construct(self):
|
||||||
|
fourier = ImageMobject("Joseph Fourier")
|
||||||
|
fourier.set_height(5)
|
||||||
|
fourier.to_corner(DR)
|
||||||
|
fourier.shift(LEFT)
|
||||||
|
bubble = ThoughtBubble(
|
||||||
|
direction=RIGHT,
|
||||||
|
height=3,
|
||||||
|
width=4,
|
||||||
|
)
|
||||||
|
bubble.move_tip_to(fourier.get_corner(UL) + 0.5 * DR)
|
||||||
|
|
||||||
|
observations = VGroup(
|
||||||
|
TextMobject(
|
||||||
|
"1)",
|
||||||
|
# "Sine waves",
|
||||||
|
# "H",
|
||||||
|
# "Heat equation",
|
||||||
|
),
|
||||||
|
TextMobject(
|
||||||
|
"2)",
|
||||||
|
# "Linearity"
|
||||||
|
),
|
||||||
|
TextMobject(
|
||||||
|
"3)",
|
||||||
|
# "Any$^{*}$ function is\\\\",
|
||||||
|
# "a sum of sine waves",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# heart = SuitSymbol("hearts")
|
||||||
|
# heart.replace(observations[0][2])
|
||||||
|
# observations[0][2].become(heart)
|
||||||
|
# observations[0][1].add(happiness)
|
||||||
|
# observations[2][2].align_to(
|
||||||
|
# observations[2][1], LEFT,
|
||||||
|
# )
|
||||||
|
|
||||||
|
observations.arrange(
|
||||||
|
DOWN,
|
||||||
|
aligned_edge=LEFT,
|
||||||
|
buff=LARGE_BUFF,
|
||||||
|
)
|
||||||
|
observations.set_height(FRAME_HEIGHT - 2)
|
||||||
|
observations.to_corner(UL, buff=LARGE_BUFF)
|
||||||
|
|
||||||
|
self.add(fourier)
|
||||||
|
self.play(ShowCreation(bubble))
|
||||||
|
self.wait()
|
||||||
|
self.play(LaggedStart(*[
|
||||||
|
TransformFromCopy(bubble, observation)
|
||||||
|
for observation in observations
|
||||||
|
], lag_ratio=0.2))
|
||||||
|
self.play(
|
||||||
|
FadeOut(fourier),
|
||||||
|
FadeOut(bubble),
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class NewSceneName(Scene):
|
||||||
|
def construct(self):
|
||||||
|
pass
|
261
active_projects/ode/part3/temperature_graphs.py
Normal file
261
active_projects/ode/part3/temperature_graphs.py
Normal file
|
@ -0,0 +1,261 @@
|
||||||
|
from manimlib.imports import *
|
||||||
|
|
||||||
|
|
||||||
|
class TemperatureGraphScene(SpecialThreeDScene):
|
||||||
|
CONFIG = {
|
||||||
|
"axes_config": {
|
||||||
|
"x_min": 0,
|
||||||
|
"x_max": TAU,
|
||||||
|
"y_min": 0,
|
||||||
|
"y_max": 10,
|
||||||
|
"z_min": -3,
|
||||||
|
"z_max": 3,
|
||||||
|
"x_axis_config": {
|
||||||
|
"tick_frequency": TAU / 8,
|
||||||
|
"include_tip": False,
|
||||||
|
},
|
||||||
|
"num_axis_pieces": 1,
|
||||||
|
},
|
||||||
|
"default_graph_style": {
|
||||||
|
"stroke_width": 2,
|
||||||
|
"stroke_color": WHITE,
|
||||||
|
"background_image_file": "VerticalTempGradient",
|
||||||
|
},
|
||||||
|
"default_surface_style": {
|
||||||
|
"fill_opacity": 0.1,
|
||||||
|
"checkerboard_colors": [LIGHT_GREY],
|
||||||
|
"stroke_width": 0.5,
|
||||||
|
"stroke_color": WHITE,
|
||||||
|
"stroke_opacity": 0.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_three_d_axes(self, include_labels=True):
|
||||||
|
axes = ThreeDAxes(**self.axes_config)
|
||||||
|
axes.set_stroke(width=2)
|
||||||
|
|
||||||
|
# Add number labels
|
||||||
|
# TODO?
|
||||||
|
|
||||||
|
# Add axis labels
|
||||||
|
if include_labels:
|
||||||
|
x_label = TexMobject("x")
|
||||||
|
x_label.next_to(axes.x_axis.get_right(), DOWN)
|
||||||
|
axes.x_axis.add(x_label)
|
||||||
|
|
||||||
|
t_label = TextMobject("Time")
|
||||||
|
t_label.rotate(90 * DEGREES, OUT)
|
||||||
|
t_label.next_to(axes.y_axis.get_top(), DL)
|
||||||
|
axes.y_axis.add(t_label)
|
||||||
|
|
||||||
|
temp_label = TextMobject("Temperature")
|
||||||
|
temp_label.rotate(90 * DEGREES, RIGHT)
|
||||||
|
temp_label.next_to(axes.z_axis.get_zenith(), RIGHT)
|
||||||
|
axes.z_axis.add(temp_label)
|
||||||
|
|
||||||
|
# Adjust axis orinetations
|
||||||
|
axes.x_axis.rotate(
|
||||||
|
90 * DEGREES, RIGHT,
|
||||||
|
about_point=axes.c2p(0, 0, 0),
|
||||||
|
)
|
||||||
|
axes.y_axis.rotate(
|
||||||
|
90 * DEGREES, UP,
|
||||||
|
about_point=axes.c2p(0, 0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add xy-plane
|
||||||
|
surface_config = {
|
||||||
|
"u_min": 0,
|
||||||
|
"u_max": axes.x_max,
|
||||||
|
"v_min": 0,
|
||||||
|
"v_max": axes.y_max,
|
||||||
|
"resolution": (16, 10),
|
||||||
|
}
|
||||||
|
axes.surface_config = surface_config
|
||||||
|
input_plane = ParametricSurface(
|
||||||
|
lambda x, t: axes.c2p(x, t, 0),
|
||||||
|
# lambda x, t: np.array([x, t, 0]),
|
||||||
|
**surface_config,
|
||||||
|
)
|
||||||
|
input_plane.set_style(
|
||||||
|
fill_opacity=0.5,
|
||||||
|
fill_color=BLUE_B,
|
||||||
|
stroke_width=0.5,
|
||||||
|
stroke_color=WHITE,
|
||||||
|
)
|
||||||
|
|
||||||
|
axes.input_plane = input_plane
|
||||||
|
|
||||||
|
return axes
|
||||||
|
|
||||||
|
def get_initial_state_graph(self, axes, func, **kwargs):
|
||||||
|
config = dict()
|
||||||
|
config.update(self.default_graph_style)
|
||||||
|
config.update(kwargs)
|
||||||
|
return ParametricFunction(
|
||||||
|
lambda x: axes.c2p(
|
||||||
|
x, 0, func(x)
|
||||||
|
),
|
||||||
|
t_min=axes.x_min,
|
||||||
|
t_max=axes.x_max,
|
||||||
|
**config,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_surface(self, axes, func, **kwargs):
|
||||||
|
config = dict()
|
||||||
|
config.update(axes.surface_config)
|
||||||
|
config.update(self.default_surface_style)
|
||||||
|
config.update(kwargs)
|
||||||
|
return ParametricSurface(
|
||||||
|
lambda x, t: axes.c2p(
|
||||||
|
x, t, func(x, t)
|
||||||
|
),
|
||||||
|
**config
|
||||||
|
)
|
||||||
|
|
||||||
|
def orient_three_d_mobject(self, mobject,
|
||||||
|
phi=85 * DEGREES,
|
||||||
|
theta=-80 * DEGREES):
|
||||||
|
mobject.rotate(-90 * DEGREES - theta, OUT)
|
||||||
|
mobject.rotate(phi, LEFT)
|
||||||
|
return mobject
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleSinExpGraph(TemperatureGraphScene):
|
||||||
|
def construct(self):
|
||||||
|
axes = self.get_three_d_axes()
|
||||||
|
sine_graph = self.get_sine_graph(axes)
|
||||||
|
sine_exp_surface = self.get_sine_exp_surface(axes)
|
||||||
|
|
||||||
|
self.set_camera_orientation(
|
||||||
|
phi=80 * DEGREES,
|
||||||
|
theta=-80 * DEGREES,
|
||||||
|
)
|
||||||
|
self.camera.frame_center.shift(3 * RIGHT)
|
||||||
|
self.begin_ambient_camera_rotation(rate=0.01)
|
||||||
|
|
||||||
|
self.add(axes)
|
||||||
|
self.play(ShowCreation(sine_graph))
|
||||||
|
self.play(UpdateFromAlphaFunc(
|
||||||
|
sine_exp_surface,
|
||||||
|
lambda m, a: m.become(
|
||||||
|
self.get_sine_exp_surface(axes, v_max=a * 10)
|
||||||
|
),
|
||||||
|
run_time=3
|
||||||
|
))
|
||||||
|
self.wait(20)
|
||||||
|
|
||||||
|
#
|
||||||
|
def sin_exp(self, x, t, A=2, omega=1, k=0.25):
|
||||||
|
return A * np.sin(omega * x) * np.exp(-k * (omega**2) * t)
|
||||||
|
|
||||||
|
def get_sine_graph(self, axes, **config):
|
||||||
|
return self.get_initial_state_graph(
|
||||||
|
axes,
|
||||||
|
lambda x: self.sin_exp(x, 0),
|
||||||
|
**config
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_sine_exp_surface(self, axes, **config):
|
||||||
|
return self.get_surface(
|
||||||
|
axes,
|
||||||
|
lambda x, t: self.sin_exp(x, t),
|
||||||
|
**config
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class AddMultipleSolutions(SimpleSinExpGraph):
|
||||||
|
CONFIG = {
|
||||||
|
"axes_config": {
|
||||||
|
"x_axis_config": {
|
||||||
|
"unit_size": 0.7,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
axes1, axes2, axes3 = all_axes = VGroup(*[
|
||||||
|
self.get_three_d_axes(
|
||||||
|
include_labels=False,
|
||||||
|
)
|
||||||
|
for x in range(3)
|
||||||
|
])
|
||||||
|
all_axes.scale(0.5)
|
||||||
|
self.orient_three_d_mobject(all_axes)
|
||||||
|
|
||||||
|
As = [1.5, 1.5]
|
||||||
|
omegas = [1, 2]
|
||||||
|
ks = [0.25, 0.01]
|
||||||
|
quads = [
|
||||||
|
(axes1, [As[0]], [omegas[0]], [ks[0]]),
|
||||||
|
(axes2, [As[1]], [omegas[1]], [ks[1]]),
|
||||||
|
(axes3, As, omegas, ks),
|
||||||
|
]
|
||||||
|
|
||||||
|
for axes, As, omegas, ks in quads:
|
||||||
|
graph = self.get_initial_state_graph(
|
||||||
|
axes,
|
||||||
|
lambda x: np.sum([
|
||||||
|
self.sin_exp(x, 0, A, omega, k)
|
||||||
|
for A, omega, k in zip(As, omegas, ks)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
surface = self.get_surface(
|
||||||
|
axes,
|
||||||
|
lambda x, t: np.sum([
|
||||||
|
self.sin_exp(x, t, A, omega)
|
||||||
|
for A, omega in zip(As, omegas)
|
||||||
|
])
|
||||||
|
)
|
||||||
|
surface.sort(lambda p: -p[2])
|
||||||
|
|
||||||
|
axes.add(surface, graph)
|
||||||
|
axes.graph = graph
|
||||||
|
axes.surface = surface
|
||||||
|
|
||||||
|
self.set_camera_orientation(distance=100)
|
||||||
|
plus = TexMobject("+").scale(2)
|
||||||
|
equals = TexMobject("=").scale(2)
|
||||||
|
group = VGroup(
|
||||||
|
axes1, plus, axes2, equals, axes3,
|
||||||
|
)
|
||||||
|
group.arrange(RIGHT, buff=SMALL_BUFF)
|
||||||
|
|
||||||
|
for axes in all_axes:
|
||||||
|
checkmark = TexMobject("\\checkmark")
|
||||||
|
checkmark.set_color(GREEN)
|
||||||
|
checkmark.scale(2)
|
||||||
|
checkmark.next_to(axes, UP)
|
||||||
|
checkmark.shift(0.7 * DOWN)
|
||||||
|
axes.checkmark = checkmark
|
||||||
|
|
||||||
|
self.add(axes1, axes2)
|
||||||
|
self.play(
|
||||||
|
LaggedStart(
|
||||||
|
Write(axes1.surface),
|
||||||
|
Write(axes2.surface),
|
||||||
|
),
|
||||||
|
LaggedStart(
|
||||||
|
FadeInFrom(axes1.checkmark, DOWN),
|
||||||
|
FadeInFrom(axes2.checkmark, DOWN),
|
||||||
|
),
|
||||||
|
lag_ratio=0.2,
|
||||||
|
run_time=1,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(Write(plus))
|
||||||
|
self.play(
|
||||||
|
Transform(
|
||||||
|
axes1.copy().set_fill(opacity=0),
|
||||||
|
axes3
|
||||||
|
),
|
||||||
|
Transform(
|
||||||
|
axes2.copy().set_fill(opacity=0),
|
||||||
|
axes3
|
||||||
|
),
|
||||||
|
FadeInFrom(equals, LEFT)
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
FadeInFrom(axes3.checkmark, DOWN),
|
||||||
|
)
|
||||||
|
self.wait()
|
|
@ -171,12 +171,18 @@ class Axes(VGroup, CoordinateSystem):
|
||||||
result += (axis.number_to_point(coord) - origin)
|
result += (axis.number_to_point(coord) - origin)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def c2p(self, *coords):
|
||||||
|
return self.coords_to_point(*coords)
|
||||||
|
|
||||||
def point_to_coords(self, point):
|
def point_to_coords(self, point):
|
||||||
return tuple([
|
return tuple([
|
||||||
axis.point_to_number(point)
|
axis.point_to_number(point)
|
||||||
for axis in self.get_axes()
|
for axis in self.get_axes()
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def p2c(self, point):
|
||||||
|
return self.point_to_coords(point)
|
||||||
|
|
||||||
def get_axes(self):
|
def get_axes(self):
|
||||||
return self.axes
|
return self.axes
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ class ParametricFunction(VMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"t_min": 0,
|
"t_min": 0,
|
||||||
"t_max": 1,
|
"t_max": 1,
|
||||||
"step_size": 0.01, # use "auto" (lwoercase) for automatic step size
|
"step_size": 0.01, # Use "auto" (lowercase) for automatic step size
|
||||||
"dt": 1e-8,
|
"dt": 1e-8,
|
||||||
# TODO, be smarter about figuring these out?
|
# TODO, be smarter about figuring these out?
|
||||||
"discontinuities": [],
|
"discontinuities": [],
|
||||||
|
|
|
@ -205,6 +205,10 @@ def center_of_mass(points):
|
||||||
return sum(points) / len(points)
|
return sum(points) / len(points)
|
||||||
|
|
||||||
|
|
||||||
|
def midpoint(point1, point2):
|
||||||
|
return center_of_mass([point1, point2])
|
||||||
|
|
||||||
|
|
||||||
def line_intersection(line1, line2):
|
def line_intersection(line1, line2):
|
||||||
"""
|
"""
|
||||||
return intersection point of two lines,
|
return intersection point of two lines,
|
||||||
|
|
Loading…
Add table
Reference in a new issue