mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Merge branch 'master' into lighthouse
This commit is contained in:
commit
9f46c69fff
5 changed files with 611 additions and 75 deletions
|
@ -38,7 +38,7 @@ python extract_scene.py example_scenes.py SquareToCircle -p
|
||||||
The -p is for previewing, meaning the the video file will automatically open when it is done rendering.
|
The -p is for previewing, meaning the the video file will automatically open when it is done rendering.
|
||||||
Use -l for a faster rendering at a lower quality.
|
Use -l for a faster rendering at a lower quality.
|
||||||
Use -s to skip to the end and just show the final frame.
|
Use -s to skip to the end and just show the final frame.
|
||||||
Use -n <number> to skip ahead to the n'th animation of a scene.
|
Use -n (number) to skip ahead to the n'th animation of a scene.
|
||||||
Use -f to show the file in finder (for osx)
|
Use -f to show the file in finder (for osx)
|
||||||
|
|
||||||
You will probably want to change the ANIMATIONS_DIR constant to be whatever directory you want video files to output to.
|
You will probably want to change the ANIMATIONS_DIR constant to be whatever directory you want video files to output to.
|
||||||
|
|
|
@ -17,6 +17,7 @@ from topics.three_dimensions import *
|
||||||
from topics.objects import *
|
from topics.objects import *
|
||||||
from topics.probability import *
|
from topics.probability import *
|
||||||
from topics.complex_numbers import *
|
from topics.complex_numbers import *
|
||||||
|
from topics.common_scenes import *
|
||||||
from scene import Scene
|
from scene import Scene
|
||||||
from scene.reconfigurable_scene import ReconfigurableScene
|
from scene.reconfigurable_scene import ReconfigurableScene
|
||||||
from scene.zoomed_scene import *
|
from scene.zoomed_scene import *
|
||||||
|
@ -246,18 +247,19 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(MoveToTarget(sum_lines, path_arc = np.pi/4))
|
self.play(MoveToTarget(sum_lines, path_arc = np.pi/4))
|
||||||
self.wait(2)
|
self.wait(2)
|
||||||
self.play(*[
|
# self.play(*[
|
||||||
Transform(
|
# Transform(
|
||||||
line,
|
# line,
|
||||||
VectorizedPoint(axes.coords_to_point(0, self.equilibrium_height)),
|
# VectorizedPoint(axes.coords_to_point(0, self.equilibrium_height)),
|
||||||
remover = True
|
# remover = True
|
||||||
)
|
# )
|
||||||
for line, axes in [
|
# for line, axes in [
|
||||||
(A_line, A_axes),
|
# (A_line, A_axes),
|
||||||
(D_line, D_axes),
|
# (D_line, D_axes),
|
||||||
(sum_lines, axes),
|
# (sum_lines, axes),
|
||||||
]
|
# ]
|
||||||
])
|
# ])
|
||||||
|
self.lines_to_fade = VGroup(A_line, D_line, sum_lines)
|
||||||
|
|
||||||
def draw_full_sum(self):
|
def draw_full_sum(self):
|
||||||
axes = self.axes
|
axes = self.axes
|
||||||
|
@ -270,8 +272,25 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
|
|
||||||
sum_graph = axes.get_graph(new_func)
|
sum_graph = axes.get_graph(new_func)
|
||||||
sum_graph.highlight(self.sum_color)
|
sum_graph.highlight(self.sum_color)
|
||||||
|
thin_sum_graph = sum_graph.copy().fade()
|
||||||
|
|
||||||
##TODO
|
A_graph = self.A_graph
|
||||||
|
D_graph = self.D_graph
|
||||||
|
D_axes = self.D_axes
|
||||||
|
|
||||||
|
rect = Rectangle(
|
||||||
|
height = 2.5*SPACE_HEIGHT,
|
||||||
|
width = MED_SMALL_BUFF,
|
||||||
|
stroke_width = 0,
|
||||||
|
fill_color = YELLOW,
|
||||||
|
fill_opacity = 0.4
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(A_graph.copy(), thin_sum_graph),
|
||||||
|
ReplacementTransform(D_graph.copy(), thin_sum_graph),
|
||||||
|
# FadeOut(self.lines_to_fade)
|
||||||
|
)
|
||||||
self.play(
|
self.play(
|
||||||
self.get_graph_line_animation(self.A_axes, self.A_graph),
|
self.get_graph_line_animation(self.A_axes, self.A_graph),
|
||||||
self.get_graph_line_animation(self.D_axes, self.D_graph),
|
self.get_graph_line_animation(self.D_axes, self.D_graph),
|
||||||
|
@ -280,7 +299,13 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
run_time = 15,
|
run_time = 15,
|
||||||
rate_func = None
|
rate_func = None
|
||||||
)
|
)
|
||||||
|
self.remove(thin_sum_graph)
|
||||||
self.wait()
|
self.wait()
|
||||||
|
for x in 2.85, 3.57:
|
||||||
|
rect.move_to(D_axes.coords_to_point(x, 0))
|
||||||
|
self.play(GrowFromPoint(rect, rect.get_top()))
|
||||||
|
self.wait()
|
||||||
|
self.play(FadeOut(rect))
|
||||||
|
|
||||||
self.sum_graph = sum_graph
|
self.sum_graph = sum_graph
|
||||||
|
|
||||||
|
@ -299,7 +324,10 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
label.stretch_in_place(0.5, 0)
|
label.stretch_in_place(0.5, 0)
|
||||||
label.move_to(bottom, DOWN)
|
label.move_to(bottom, DOWN)
|
||||||
|
|
||||||
self.play(MoveToTarget(squish_group))
|
self.play(
|
||||||
|
MoveToTarget(squish_group),
|
||||||
|
FadeOut(self.lines_to_fade),
|
||||||
|
)
|
||||||
|
|
||||||
F_axes = self.D_axes.deepcopy()
|
F_axes = self.D_axes.deepcopy()
|
||||||
C_axes = self.A_axes.deepcopy()
|
C_axes = self.A_axes.deepcopy()
|
||||||
|
@ -316,7 +344,7 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
label.highlight(graph.get_stroke_color())
|
label.highlight(graph.get_stroke_color())
|
||||||
label.next_to(graph, UP, SMALL_BUFF)
|
label.next_to(graph, UP, SMALL_BUFF)
|
||||||
|
|
||||||
graphs = [self.A_graph, self.D_graph, F_graph, C_graph]
|
graphs = VGroup(self.A_graph, self.D_graph, F_graph, C_graph)
|
||||||
def new_sum_func(x):
|
def new_sum_func(x):
|
||||||
result = sum([
|
result = sum([
|
||||||
graph.underlying_function(x) - self.equilibrium_height
|
graph.underlying_function(x) - self.equilibrium_height
|
||||||
|
@ -329,11 +357,15 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
num_graph_points = 200
|
num_graph_points = 200
|
||||||
)
|
)
|
||||||
new_sum_graph.highlight(BLUE_C)
|
new_sum_graph.highlight(BLUE_C)
|
||||||
|
thin_new_sum_graph = new_sum_graph.copy().fade()
|
||||||
|
|
||||||
self.play(*it.chain(
|
self.play(*it.chain(
|
||||||
map(ShowCreation, [F_axes, C_axes, F_graph, C_graph,]),
|
map(ShowCreation, [F_axes, C_axes, F_graph, C_graph]),
|
||||||
map(Write, [F_label, C_label]),
|
map(Write, [F_label, C_label]),
|
||||||
[FadeOut(self.sum_graph)]
|
map(FadeOut, [self.sum_graph])
|
||||||
|
))
|
||||||
|
self.play(ReplacementTransform(
|
||||||
|
graphs.copy(), thin_new_sum_graph
|
||||||
))
|
))
|
||||||
kwargs = {"rate_func" : None, "run_time" : 10}
|
kwargs = {"rate_func" : None, "run_time" : 10}
|
||||||
self.play(ShowCreation(new_sum_graph.copy(), **kwargs), *[
|
self.play(ShowCreation(new_sum_graph.copy(), **kwargs), *[
|
||||||
|
@ -369,7 +401,7 @@ class AddingPureFrequencies(PiCreatureScene):
|
||||||
value *= smooth((x_max - x )/tail_len)
|
value *= smooth((x_max - x )/tail_len)
|
||||||
return value + self.equilibrium_height
|
return value + self.equilibrium_height
|
||||||
ngp = 2*(x_max - x_min)*frequency + 1
|
ngp = 2*(x_max - x_min)*frequency + 1
|
||||||
graph = axes.get_graph(func, num_graph_points = ngp)
|
graph = axes.get_graph(func, num_graph_points = int(ngp))
|
||||||
return graph
|
return graph
|
||||||
|
|
||||||
def get_A_graph_v_line(self, x):
|
def get_A_graph_v_line(self, x):
|
||||||
|
@ -658,6 +690,7 @@ class FourierMachineScene(Scene):
|
||||||
"y_max" : 2,
|
"y_max" : 2,
|
||||||
"y_axis_config" : {"unit_size" : 0.8},
|
"y_axis_config" : {"unit_size" : 0.8},
|
||||||
},
|
},
|
||||||
|
"time_label_t" : 3.4,
|
||||||
"circle_plane_config" : {
|
"circle_plane_config" : {
|
||||||
"x_radius" : 2.5,
|
"x_radius" : 2.5,
|
||||||
"y_radius" : 2.5,
|
"y_radius" : 2.5,
|
||||||
|
@ -708,7 +741,10 @@ class FourierMachineScene(Scene):
|
||||||
labels = VGroup(time_label, intensity_label)
|
labels = VGroup(time_label, intensity_label)
|
||||||
for label in labels:
|
for label in labels:
|
||||||
label.scale(self.text_scale_val)
|
label.scale(self.text_scale_val)
|
||||||
time_label.next_to(time_axes.coords_to_point(3.5,0), DOWN)
|
time_label.next_to(
|
||||||
|
time_axes.coords_to_point(self.time_label_t,0),
|
||||||
|
DOWN
|
||||||
|
)
|
||||||
intensity_label.next_to(time_axes.y_axis.get_top(), RIGHT)
|
intensity_label.next_to(time_axes.y_axis.get_top(), RIGHT)
|
||||||
time_axes.labels = labels
|
time_axes.labels = labels
|
||||||
time_axes.add(labels)
|
time_axes.add(labels)
|
||||||
|
@ -894,6 +930,7 @@ class FourierMachineScene(Scene):
|
||||||
t_max = t_max or time_axes.x_max
|
t_max = t_max or time_axes.x_max
|
||||||
v_line = DashedLine(
|
v_line = DashedLine(
|
||||||
ctp(0, 0), ctp(0, time_axes.y_max),
|
ctp(0, 0), ctp(0, time_axes.y_max),
|
||||||
|
stroke_width = 6,
|
||||||
)
|
)
|
||||||
v_line.highlight(RED)
|
v_line.highlight(RED)
|
||||||
|
|
||||||
|
@ -1102,9 +1139,9 @@ class WrapCosineGraphAroundCircle(FourierMachineScene):
|
||||||
for p1, p2 in zip(peak_points, peak_points[1:])
|
for p1, p2 in zip(peak_points, peak_points[1:])
|
||||||
])
|
])
|
||||||
|
|
||||||
def get_bps_label(self):
|
def get_bps_label(self, freq = 3):
|
||||||
braces = VGroup(*self.get_peak_braces()[3:6])
|
braces = VGroup(*self.get_peak_braces()[freq:2*freq])
|
||||||
words = TextMobject("3 beats/second")
|
words = TextMobject("%d beats/second"%freq)
|
||||||
words.scale_to_fit_width(0.9*braces.get_width())
|
words.scale_to_fit_width(0.9*braces.get_width())
|
||||||
words.move_to(braces, DOWN)
|
words.move_to(braces, DOWN)
|
||||||
return words
|
return words
|
||||||
|
@ -1199,9 +1236,7 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
self.center_of_mass_dot = dot
|
self.center_of_mass_dot = dot
|
||||||
self.center_of_mass_dot_anim = UpdateFromFunc(
|
self.generate_center_of_mass_dot_update_anim()
|
||||||
dot, lambda d : d.move_to(self.get_pol_graph_center_of_mass())
|
|
||||||
)
|
|
||||||
self.center_of_mass_label = words
|
self.center_of_mass_label = words
|
||||||
|
|
||||||
def change_to_various_frequencies(self):
|
def change_to_various_frequencies(self):
|
||||||
|
@ -1257,21 +1292,15 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
graph = self.graph
|
graph = self.graph
|
||||||
fourier_graph = self.get_fourier_transform_graph(graph)
|
fourier_graph = self.get_fourier_transform_graph(graph)
|
||||||
fourier_graph.save_state()
|
fourier_graph.save_state()
|
||||||
|
fourier_graph_update = self.get_fouier_graph_drawing_update_anim(
|
||||||
|
fourier_graph
|
||||||
|
)
|
||||||
v_line = DashedLine(
|
v_line = DashedLine(
|
||||||
self.frequency_axes.coords_to_point(0, 0),
|
self.frequency_axes.coords_to_point(0, 0),
|
||||||
self.frequency_axes.coords_to_point(0, 1),
|
self.frequency_axes.coords_to_point(0, 1),
|
||||||
stroke_width = 6,
|
stroke_width = 6,
|
||||||
color = fourier_graph.get_color()
|
color = fourier_graph.get_color()
|
||||||
)
|
)
|
||||||
fourier_graph_copy = fourier_graph.copy()
|
|
||||||
max_freq = self.frequency_axes.x_max
|
|
||||||
def update_fourier_graph(fg):
|
|
||||||
freq = self.graph.polarized_mobject.frequency
|
|
||||||
fg.pointwise_become_partial(
|
|
||||||
fourier_graph_copy,
|
|
||||||
0, freq/max_freq
|
|
||||||
)
|
|
||||||
return fg
|
|
||||||
|
|
||||||
self.change_frequency(0.0)
|
self.change_frequency(0.0)
|
||||||
self.generate_fourier_dot_transform(fourier_graph)
|
self.generate_fourier_dot_transform(fourier_graph)
|
||||||
|
@ -1286,10 +1315,8 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
fourier_graph.restore()
|
fourier_graph.restore()
|
||||||
self.change_frequency(
|
self.change_frequency(
|
||||||
freq,
|
freq,
|
||||||
added_anims = [UpdateFromFunc(
|
added_anims = [fourier_graph_update],
|
||||||
fourier_graph, update_fourier_graph
|
run_time = 8,
|
||||||
)],
|
|
||||||
run_time = 5,
|
|
||||||
)
|
)
|
||||||
self.wait()
|
self.wait()
|
||||||
self.fourier_graph = fourier_graph
|
self.fourier_graph = fourier_graph
|
||||||
|
@ -1318,8 +1345,10 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
self.play(FadeIn(rect))
|
self.play(FadeIn(rect))
|
||||||
self.wait()
|
self.wait()
|
||||||
for group in groups:
|
for group in groups:
|
||||||
|
graph_copy = group[0].copy().highlight(PINK)
|
||||||
self.play(FadeIn(group))
|
self.play(FadeIn(group))
|
||||||
self.play(ShowCreation(group[0]))
|
self.play(ShowCreation(graph_copy))
|
||||||
|
self.play(FadeOut(graph_copy))
|
||||||
self.wait()
|
self.wait()
|
||||||
self.play(FadeOut(group))
|
self.play(FadeOut(group))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
@ -1399,9 +1428,6 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
pg.point_from_proportion(alpha)
|
pg.point_from_proportion(alpha)
|
||||||
for alpha in np.linspace(0, 1, 1000)
|
for alpha in np.linspace(0, 1, 1000)
|
||||||
])
|
])
|
||||||
# result -= self.circle_plane.get_center()
|
|
||||||
# result *= 2
|
|
||||||
# result += self.circle_plane.get_center()
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def generate_fourier_dot_transform(self, fourier_graph):
|
def generate_fourier_dot_transform(self, fourier_graph):
|
||||||
|
@ -1416,6 +1442,24 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene):
|
||||||
)
|
)
|
||||||
self.fourier_graph_dot_anim.update(0)
|
self.fourier_graph_dot_anim.update(0)
|
||||||
|
|
||||||
|
def get_fouier_graph_drawing_update_anim(self, fourier_graph):
|
||||||
|
fourier_graph_copy = fourier_graph.copy()
|
||||||
|
max_freq = self.frequency_axes.x_max
|
||||||
|
def update_fourier_graph(fg):
|
||||||
|
freq = self.graph.polarized_mobject.frequency
|
||||||
|
fg.pointwise_become_partial(
|
||||||
|
fourier_graph_copy,
|
||||||
|
0, freq/max_freq
|
||||||
|
)
|
||||||
|
return fg
|
||||||
|
return UpdateFromFunc(fourier_graph, update_fourier_graph)
|
||||||
|
|
||||||
|
def generate_center_of_mass_dot_update_anim(self):
|
||||||
|
self.center_of_mass_dot_anim = UpdateFromFunc(
|
||||||
|
self.center_of_mass_dot,
|
||||||
|
lambda d : d.move_to(self.get_pol_graph_center_of_mass())
|
||||||
|
)
|
||||||
|
|
||||||
def change_frequency(self, new_freq, **kwargs):
|
def change_frequency(self, new_freq, **kwargs):
|
||||||
kwargs["run_time"] = kwargs.get("run_time", 3)
|
kwargs["run_time"] = kwargs.get("run_time", 3)
|
||||||
added_anims = kwargs.get("added_anims", [])
|
added_anims = kwargs.get("added_anims", [])
|
||||||
|
@ -1448,20 +1492,16 @@ class StudentsHorrifiedAtScene(TeacherStudentsScene):
|
||||||
)
|
)
|
||||||
self.wait(4)
|
self.wait(4)
|
||||||
|
|
||||||
|
class ShowLowerFrequency(DrawFrequencyPlot):
|
||||||
class ShowLinearity(DrawFrequencyPlot):
|
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"lower_signal_frequency" : 2.0,
|
"signal_frequency" : 2.0,
|
||||||
|
"higher_signal_frequency" : 3.0,
|
||||||
"lower_signal_color" : PINK,
|
"lower_signal_color" : PINK,
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.setup_all_axes()
|
self.setup_all_axes()
|
||||||
self.show_lower_frequency_signal()
|
self.show_lower_frequency_signal()
|
||||||
self.play_with_lower_frequency_signal()
|
self.play_with_lower_frequency_signal()
|
||||||
self.point_out_fourier_spike()
|
|
||||||
self.show_sum_of_signals()
|
|
||||||
self.play_with_sum_signal()
|
|
||||||
self.point_out_two_spikes()
|
|
||||||
|
|
||||||
def setup_all_axes(self):
|
def setup_all_axes(self):
|
||||||
self.add(self.get_time_axes())
|
self.add(self.get_time_axes())
|
||||||
|
@ -1471,44 +1511,509 @@ class ShowLinearity(DrawFrequencyPlot):
|
||||||
|
|
||||||
def show_lower_frequency_signal(self):
|
def show_lower_frequency_signal(self):
|
||||||
axes = self.time_axes
|
axes = self.time_axes
|
||||||
start_graph = self.get_cosine_wave(freq = self.signal_frequency)
|
start_graph = self.get_cosine_wave(freq = self.higher_signal_frequency)
|
||||||
graph = self.get_cosine_wave(freq = self.lower_signal_frequency)
|
graph = self.get_cosine_wave(
|
||||||
|
freq = self.signal_frequency,
|
||||||
|
)
|
||||||
graph.highlight(self.lower_signal_color)
|
graph.highlight(self.lower_signal_color)
|
||||||
start_graph.generate_target()
|
self.graph = graph
|
||||||
start_graph.target.stretch(
|
ratio = float(self.higher_signal_frequency)/self.signal_frequency
|
||||||
|
|
||||||
|
braces = VGroup(*self.get_peak_braces()[2:4])
|
||||||
|
v_lines = VGroup(*[
|
||||||
|
DashedLine(ORIGIN, 1.5*UP).move_to(
|
||||||
|
axes.coords_to_point(x, 0), DOWN
|
||||||
|
)
|
||||||
|
for x in 1, 2
|
||||||
|
])
|
||||||
|
bps_label = self.get_bps_label(2)
|
||||||
|
bps_label.save_state()
|
||||||
|
bps_label.next_to(braces, UP, SMALL_BUFF)
|
||||||
|
|
||||||
|
|
||||||
|
# self.add(start_graph)
|
||||||
|
self.play(
|
||||||
|
start_graph.stretch, ratio, 0, {"about_edge" : LEFT},
|
||||||
|
start_graph.highlight, graph.get_color(),
|
||||||
|
)
|
||||||
|
self.play(FadeOut(start_graph), Animation(graph))
|
||||||
|
self.remove(start_graph)
|
||||||
|
self.play(
|
||||||
|
Write(bps_label),
|
||||||
|
LaggedStart(FadeIn, braces),
|
||||||
|
*map(ShowCreation, v_lines),
|
||||||
|
run_time = 1
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
FadeOut(v_lines),
|
||||||
|
FadeOut(braces),
|
||||||
|
bps_label.restore,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.add(start_graph)
|
def play_with_lower_frequency_signal(self):
|
||||||
self.play(ReplacementTransform(
|
freq = 0.1
|
||||||
start_graph, graph, run_time = 3
|
|
||||||
))
|
#Wind up graph
|
||||||
|
graph = self.graph
|
||||||
|
pol_graph = self.get_polarized_mobject(graph, freq)
|
||||||
|
v_lines = self.get_v_lines_indicating_periods(freq)
|
||||||
|
self.v_lines_indicating_periods = v_lines
|
||||||
|
wps_label = self.get_winding_frequency_label()
|
||||||
|
ChangeDecimalToValue(wps_label[0], freq).update(1)
|
||||||
|
wps_label.add_to_back(BackgroundRectangle(wps_label))
|
||||||
|
wps_label.move_to(self.circle_plane, UP)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(
|
||||||
|
graph.copy(), pol_graph,
|
||||||
|
run_time = 2,
|
||||||
|
path_arc = -TAU/4,
|
||||||
|
),
|
||||||
|
FadeIn(wps_label),
|
||||||
|
)
|
||||||
|
self.change_frequency(freq, run_time = 0)
|
||||||
|
self.change_frequency(0.7)
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
def play_with_lower_frequency_signal(self):
|
#Show center of mass
|
||||||
pass
|
dot = Dot(
|
||||||
|
self.get_pol_graph_center_of_mass(),
|
||||||
|
color = self.center_of_mass_color
|
||||||
|
)
|
||||||
|
dot.save_state()
|
||||||
|
self.center_of_mass_dot = dot
|
||||||
|
com_words = TextMobject("Center of mass")
|
||||||
|
com_words.add_background_rectangle()
|
||||||
|
com_words.move_to(self.circle_plane, DOWN)
|
||||||
|
arrow = Arrow(
|
||||||
|
com_words.get_top(),
|
||||||
|
dot.get_center(),
|
||||||
|
buff = SMALL_BUFF,
|
||||||
|
color = self.center_of_mass_color
|
||||||
|
)
|
||||||
|
dot.move_to(arrow.get_start())
|
||||||
|
self.generate_center_of_mass_dot_update_anim()
|
||||||
|
|
||||||
def point_out_fourier_spike(self):
|
self.play(
|
||||||
pass
|
GrowArrow(arrow),
|
||||||
|
dot.restore,
|
||||||
|
Write(com_words)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.play(*map(FadeOut, [arrow, com_words]))
|
||||||
|
self.change_frequency(0.0)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
#Show fourier graph
|
||||||
|
fourier_graph = self.get_fourier_transform_graph(graph)
|
||||||
|
fourier_graph_update = self.get_fouier_graph_drawing_update_anim(
|
||||||
|
fourier_graph
|
||||||
|
)
|
||||||
|
x_coord_label = TextMobject(
|
||||||
|
"x-coordinate of center of mass"
|
||||||
|
)
|
||||||
|
x_coord_label.scale(self.text_scale_val)
|
||||||
|
x_coord_label.next_to(
|
||||||
|
self.frequency_axes.input_to_graph_point(
|
||||||
|
self.signal_frequency, fourier_graph
|
||||||
|
), UP
|
||||||
|
)
|
||||||
|
x_coord_label.highlight(self.center_of_mass_color)
|
||||||
|
self.generate_fourier_dot_transform(fourier_graph)
|
||||||
|
|
||||||
|
self.play(Write(x_coord_label))
|
||||||
|
self.change_frequency(
|
||||||
|
self.signal_frequency,
|
||||||
|
added_anims = [fourier_graph_update],
|
||||||
|
run_time = 10,
|
||||||
|
rate_func = smooth,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.change_frequency(
|
||||||
|
self.frequency_axes.x_max,
|
||||||
|
added_anims = [fourier_graph_update],
|
||||||
|
run_time = 15,
|
||||||
|
rate_func = smooth,
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.set_variables_as_attrs(
|
||||||
|
fourier_graph,
|
||||||
|
fourier_graph_update,
|
||||||
|
)
|
||||||
|
|
||||||
|
class MixingUnmixingTODOStub(TODOStub):
|
||||||
|
CONFIG = {
|
||||||
|
"message" : "Insert mixing and unmixing of signals"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ShowLinearity(DrawFrequencyPlot):
|
||||||
|
CONFIG = {
|
||||||
|
"high_freq_color": YELLOW,
|
||||||
|
"low_freq_color": PINK,
|
||||||
|
"sum_color": GREEN,
|
||||||
|
"low_freq" : 2.0,
|
||||||
|
"high_freq" : 3.0,
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.remove(self.pi_creature)
|
||||||
|
self.show_sum_of_signals()
|
||||||
|
self.show_winding_with_sum_graph()
|
||||||
|
self.point_out_two_spikes()
|
||||||
|
|
||||||
def show_sum_of_signals(self):
|
def show_sum_of_signals(self):
|
||||||
pass
|
low_freq, high_freq = self.low_freq, self.high_freq
|
||||||
|
axes = self.get_time_axes()
|
||||||
|
axes_copy = axes.copy()
|
||||||
|
low_freq_graph, high_freq_graph = [
|
||||||
|
self.get_cosine_wave(
|
||||||
|
freq = freq,
|
||||||
|
scale_val = 0.5,
|
||||||
|
shift_val = 0.55,
|
||||||
|
)
|
||||||
|
for freq in low_freq, high_freq
|
||||||
|
]
|
||||||
|
sum_graph = self.get_time_graph(
|
||||||
|
lambda t : sum([
|
||||||
|
low_freq_graph.underlying_function(t),
|
||||||
|
high_freq_graph.underlying_function(t),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
VGroup(axes_copy, high_freq_graph).next_to(
|
||||||
|
axes, DOWN, MED_LARGE_BUFF
|
||||||
|
)
|
||||||
|
|
||||||
def play_with_sum_signal(self):
|
low_freq_label = TextMobject("%d Hz"%int(low_freq))
|
||||||
pass
|
high_freq_label = TextMobject("%d Hz"%int(high_freq))
|
||||||
|
sum_label = TextMobject(
|
||||||
|
"%d Hz"%int(low_freq), "+",
|
||||||
|
"%d Hz"%int(high_freq)
|
||||||
|
)
|
||||||
|
trips = [
|
||||||
|
(low_freq_label, low_freq_graph, self.low_freq_color),
|
||||||
|
(high_freq_label, high_freq_graph, self.high_freq_color),
|
||||||
|
(sum_label, sum_graph, self.sum_color),
|
||||||
|
]
|
||||||
|
for label, graph, color in trips:
|
||||||
|
label.next_to(graph, UP)
|
||||||
|
graph.highlight(color)
|
||||||
|
label.highlight(color)
|
||||||
|
sum_label[0].match_color(low_freq_graph)
|
||||||
|
sum_label[2].match_color(high_freq_graph)
|
||||||
|
|
||||||
|
self.add(axes, low_freq_graph)
|
||||||
|
self.play(
|
||||||
|
FadeIn(axes_copy),
|
||||||
|
ShowCreation(high_freq_graph),
|
||||||
|
)
|
||||||
|
self.play(LaggedStart(
|
||||||
|
FadeIn, VGroup(high_freq_label, low_freq_label)
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(axes_copy, axes),
|
||||||
|
ReplacementTransform(high_freq_graph, sum_graph),
|
||||||
|
ReplacementTransform(low_freq_graph, sum_graph),
|
||||||
|
ReplacementTransform(
|
||||||
|
VGroup(low_freq_label, high_freq_label),
|
||||||
|
sum_label
|
||||||
|
)
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
self.graph = graph
|
||||||
|
|
||||||
|
def show_winding_with_sum_graph(self):
|
||||||
|
graph = self.graph
|
||||||
|
circle_plane = self.get_circle_plane()
|
||||||
|
frequency_axes = self.get_frequency_axes()
|
||||||
|
pol_graph = self.get_polarized_mobject(graph, freq = 0.0)
|
||||||
|
|
||||||
|
wps_label = self.get_winding_frequency_label()
|
||||||
|
ChangeDecimalToValue(wps_label[0], 0.0).update(1)
|
||||||
|
wps_label.add_to_back(BackgroundRectangle(wps_label))
|
||||||
|
wps_label.move_to(circle_plane, UP)
|
||||||
|
|
||||||
|
v_lines = self.get_v_lines_indicating_periods(0.001)
|
||||||
|
self.v_lines_indicating_periods = v_lines
|
||||||
|
|
||||||
|
dot = Dot(
|
||||||
|
self.get_pol_graph_center_of_mass(),
|
||||||
|
color = self.center_of_mass_color
|
||||||
|
)
|
||||||
|
self.center_of_mass_dot = dot
|
||||||
|
self.generate_center_of_mass_dot_update_anim()
|
||||||
|
|
||||||
|
fourier_graph = self.get_fourier_transform_graph(graph)
|
||||||
|
fourier_graph_update = self.get_fouier_graph_drawing_update_anim(
|
||||||
|
fourier_graph
|
||||||
|
)
|
||||||
|
x_coord_label = TextMobject(
|
||||||
|
"x-coordinate of center of mass"
|
||||||
|
)
|
||||||
|
x_coord_label.scale(self.text_scale_val)
|
||||||
|
x_coord_label.next_to(
|
||||||
|
self.frequency_axes.input_to_graph_point(
|
||||||
|
self.signal_frequency, fourier_graph
|
||||||
|
), UP
|
||||||
|
)
|
||||||
|
x_coord_label.highlight(self.center_of_mass_color)
|
||||||
|
almost_fourier_label = TextMobject(
|
||||||
|
"``Almost-Fourier transform''"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.generate_fourier_dot_transform(fourier_graph)
|
||||||
|
|
||||||
|
self.play(LaggedStart(
|
||||||
|
FadeIn, VGroup(
|
||||||
|
circle_plane, wps_label,
|
||||||
|
frequency_axes, x_coord_label,
|
||||||
|
),
|
||||||
|
run_time = 1,
|
||||||
|
))
|
||||||
|
self.play(
|
||||||
|
ReplacementTransform(graph.copy(), pol_graph),
|
||||||
|
GrowFromCenter(dot)
|
||||||
|
)
|
||||||
|
freqs = [
|
||||||
|
self.low_freq, self.high_freq,
|
||||||
|
self.frequency_axes.x_max
|
||||||
|
]
|
||||||
|
for freq in freqs:
|
||||||
|
self.change_frequency(
|
||||||
|
freq,
|
||||||
|
added_anims = [fourier_graph_update],
|
||||||
|
run_time = 8,
|
||||||
|
rate_func = bezier([0, 0, 1, 1]),
|
||||||
|
)
|
||||||
|
|
||||||
def point_out_two_spikes(self):
|
def point_out_two_spikes(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class ShowCommutativeDiagram(ShowLinearity):
|
||||||
|
CONFIG = {
|
||||||
|
"time_axes_config" : {
|
||||||
|
"x_max" : 1.9,
|
||||||
|
"y_max" : 2.0,
|
||||||
|
"y_min" : -2.0,
|
||||||
|
"y_axis_config" : {
|
||||||
|
"unit_size" : 0.5,
|
||||||
|
},
|
||||||
|
"x_axis_config" : {
|
||||||
|
"numbers_to_show" : [1],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"time_label_t" : 1.5,
|
||||||
|
"frequency_axes_config" : {
|
||||||
|
"x_min" : 0.0,
|
||||||
|
"x_max" : 4.0,
|
||||||
|
"y_min" : -0.1,
|
||||||
|
"y_max" : 0.5,
|
||||||
|
"y_axis_config" : {
|
||||||
|
"unit_size" : 1.5,
|
||||||
|
"tick_frequency" : 0.5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def construct(self):
|
||||||
|
self.show_diagram()
|
||||||
|
self.point_out_spikes()
|
||||||
|
|
||||||
|
def show_diagram(self):
|
||||||
|
self.remove(self.pi_creature)
|
||||||
|
|
||||||
|
#Setup axes
|
||||||
|
time_axes = self.get_time_axes()
|
||||||
|
time_axes.scale(0.8)
|
||||||
|
ta_group = VGroup(
|
||||||
|
time_axes, time_axes.deepcopy(), time_axes.deepcopy(),
|
||||||
|
)
|
||||||
|
ta_group.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF)
|
||||||
|
ta_group.to_corner(UP+LEFT, buff = MED_SMALL_BUFF)
|
||||||
|
|
||||||
|
freq_axes = Axes(**self.frequency_axes_config)
|
||||||
|
freq_axes.highlight(TEAL)
|
||||||
|
freq_label = TextMobject("Frequency")
|
||||||
|
freq_label.scale(self.text_scale_val)
|
||||||
|
freq_label.next_to(freq_axes.x_axis, DOWN, SMALL_BUFF, RIGHT)
|
||||||
|
freq_axes.label = freq_label
|
||||||
|
freq_axes.add(freq_label)
|
||||||
|
freq_axes.scale(0.8)
|
||||||
|
fa_group = VGroup(
|
||||||
|
freq_axes, freq_axes.deepcopy(), freq_axes.deepcopy()
|
||||||
|
)
|
||||||
|
VGroup(ta_group[1], fa_group[1]).shift(MED_LARGE_BUFF*UP)
|
||||||
|
for ta, fa in zip(ta_group, fa_group):
|
||||||
|
fa.next_to(
|
||||||
|
ta.x_axis.main_line, RIGHT,
|
||||||
|
submobject_to_align = fa.x_axis.main_line
|
||||||
|
)
|
||||||
|
fa.to_edge(RIGHT)
|
||||||
|
ta.remove(ta.labels)
|
||||||
|
fa.remove(fa.label)
|
||||||
|
|
||||||
|
## Add graphs
|
||||||
|
funcs = [
|
||||||
|
lambda t : np.cos(2*TAU*t),
|
||||||
|
lambda t : np.cos(3*TAU*t),
|
||||||
|
]
|
||||||
|
funcs.append(lambda t : funcs[0](t)+funcs[1](t))
|
||||||
|
colors = [
|
||||||
|
self.low_freq_color,
|
||||||
|
self.high_freq_color,
|
||||||
|
self.sum_color,
|
||||||
|
]
|
||||||
|
labels = [
|
||||||
|
TextMobject("2 Hz"),
|
||||||
|
TextMobject("3 Hz"),
|
||||||
|
# TextMobject("2 Hz", "+", "3 Hz"),
|
||||||
|
VectorizedPoint()
|
||||||
|
]
|
||||||
|
for func, color, label, ta, fa in zip(funcs, colors, labels, ta_group, fa_group):
|
||||||
|
time_graph = ta.get_graph(func)
|
||||||
|
time_graph.highlight(color)
|
||||||
|
label.highlight(color)
|
||||||
|
label.scale(0.75)
|
||||||
|
label.next_to(time_graph, UP, SMALL_BUFF)
|
||||||
|
fourier = self.get_fourier_transform(
|
||||||
|
func, ta.x_min, 4*ta.x_max
|
||||||
|
)
|
||||||
|
fourier_graph = fa.get_graph(fourier)
|
||||||
|
fourier_graph.highlight(self.center_of_mass_color)
|
||||||
|
|
||||||
|
arrow = Arrow(
|
||||||
|
ta.x_axis.main_line, fa.x_axis.main_line,
|
||||||
|
color = WHITE,
|
||||||
|
buff = MED_LARGE_BUFF,
|
||||||
|
)
|
||||||
|
words = TextMobject("Almost-Fourier \\\\ transform")
|
||||||
|
words.scale(0.6)
|
||||||
|
words.next_to(arrow, UP)
|
||||||
|
arrow.words = words
|
||||||
|
|
||||||
|
ta.graph = time_graph
|
||||||
|
ta.graph_label = label
|
||||||
|
ta.arrow = arrow
|
||||||
|
ta.add(time_graph, label)
|
||||||
|
fa.graph = fourier_graph
|
||||||
|
fa.add(fourier_graph)
|
||||||
|
# labels[-1][0].match_color(labels[0])
|
||||||
|
# labels[-1][2].match_color(labels[1])
|
||||||
|
|
||||||
|
|
||||||
|
#Add arrows
|
||||||
|
sum_arrows = VGroup()
|
||||||
|
for group in ta_group, fa_group:
|
||||||
|
arrow = Arrow(
|
||||||
|
group[1].graph, group[2].graph,
|
||||||
|
color = WHITE,
|
||||||
|
buff = SMALL_BUFF
|
||||||
|
)
|
||||||
|
arrow.scale(0.8, about_edge = UP)
|
||||||
|
arrow.words = TextMobject("Sum").scale(0.75)
|
||||||
|
arrow.words.next_to(arrow, RIGHT, buff = MED_SMALL_BUFF)
|
||||||
|
sum_arrows.add(arrow)
|
||||||
|
|
||||||
|
def apply_transform(index):
|
||||||
|
ta = ta_group[index].deepcopy()
|
||||||
|
fa = fa_group[index]
|
||||||
|
anims = [
|
||||||
|
ReplacementTransform(
|
||||||
|
getattr(ta, attr), getattr(fa, attr)
|
||||||
|
)
|
||||||
|
for attr in "x_axis", "y_axis", "graph"
|
||||||
|
]
|
||||||
|
anims += [
|
||||||
|
GrowArrow(ta.arrow),
|
||||||
|
Write(ta.arrow.words),
|
||||||
|
]
|
||||||
|
if index == 0:
|
||||||
|
anims.append(ReplacementTransform(
|
||||||
|
ta.labels[0],
|
||||||
|
fa.label
|
||||||
|
))
|
||||||
|
self.play(*anims, run_time = 1.5)
|
||||||
|
|
||||||
|
|
||||||
|
#Animations
|
||||||
|
self.add(*ta_group[:2])
|
||||||
|
self.add(ta_group[0].labels)
|
||||||
|
self.wait()
|
||||||
|
apply_transform(0)
|
||||||
|
apply_transform(1)
|
||||||
|
self.wait()
|
||||||
|
self.play(
|
||||||
|
GrowArrow(sum_arrows[1]),
|
||||||
|
Write(sum_arrows[1].words),
|
||||||
|
*[
|
||||||
|
ReplacementTransform(
|
||||||
|
fa.copy(), fa_group[2]
|
||||||
|
)
|
||||||
|
for fa in fa_group[:2]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.wait(2)
|
||||||
|
self.play(
|
||||||
|
GrowArrow(sum_arrows[0]),
|
||||||
|
Write(sum_arrows[0].words),
|
||||||
|
*[
|
||||||
|
ReplacementTransform(
|
||||||
|
mob.copy(), ta_group[2],
|
||||||
|
run_time = 1
|
||||||
|
)
|
||||||
|
for mob in ta_group[:2]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.wait()
|
||||||
|
apply_transform(2)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
self.time_axes_group = ta_group
|
||||||
|
self.frequency_axes_group = fa_group
|
||||||
|
|
||||||
|
def point_out_spikes(self):
|
||||||
|
fa_group = self.frequency_axes_group
|
||||||
|
freqs = self.low_freq, self.high_freq
|
||||||
|
flat_rects = VGroup()
|
||||||
|
for freq, axes in zip(freqs, fa_group[:2]):
|
||||||
|
flat_rect = SurroundingRectangle(axes.x_axis)
|
||||||
|
flat_rect.stretch(0.5, 1)
|
||||||
|
spike_rect = self.get_spike_rect(axes, freq)
|
||||||
|
flat_rect.match_style(spike_rect)
|
||||||
|
flat_rect.target = spike_rect
|
||||||
|
flat_rects.add(flat_rect)
|
||||||
|
|
||||||
|
self.play(LaggedStart(GrowFromCenter, flat_rects))
|
||||||
|
self.wait()
|
||||||
|
self.play(LaggedStart(MoveToTarget, flat_rects))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
sum_spike_rects = VGroup(*[
|
||||||
|
self.get_spike_rect(fa_group[2], freq)
|
||||||
|
for freq in freqs
|
||||||
|
])
|
||||||
|
self.play(ReplacementTransform(
|
||||||
|
flat_rects, sum_spike_rects
|
||||||
|
))
|
||||||
|
self.play(LaggedStart(
|
||||||
|
WiggleOutThenIn, sum_spike_rects,
|
||||||
|
run_time = 1,
|
||||||
|
lag_ratio = 0.7,
|
||||||
|
))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
##
|
||||||
|
|
||||||
|
def get_spike_rect(self, axes, freq):
|
||||||
|
peak_point = axes.input_to_graph_point(
|
||||||
|
freq, axes.graph
|
||||||
|
)
|
||||||
|
f_axis_point = axes.coords_to_point(freq, 0)
|
||||||
|
line = Line(f_axis_point, peak_point)
|
||||||
|
spike_rect = SurroundingRectangle(line)
|
||||||
|
spike_rect.set_stroke(width = 0)
|
||||||
|
spike_rect.set_fill(YELLOW, 0.5)
|
||||||
|
return spike_rect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -428,6 +428,26 @@ class Mobject(object):
|
||||||
self.shift(start-self.points[0])
|
self.shift(start-self.points[0])
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
## Match other mobvject properties
|
||||||
|
|
||||||
|
def match_color(self, mobject):
|
||||||
|
return self.highlight(mobject.get_color())
|
||||||
|
|
||||||
|
def match_dim(self, mobject, dim, **kwargs):
|
||||||
|
return self.rescale_to_fit(
|
||||||
|
mobject.length_over_dim(dim), dim,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def match_width(self, mobject):
|
||||||
|
return self.match_dim(mobject, 0)
|
||||||
|
|
||||||
|
def match_height(self, mobject):
|
||||||
|
return self.match_dim(mobject, 1)
|
||||||
|
|
||||||
|
def match_depth(self, mobject):
|
||||||
|
return self.match_dim(mobject, 2)
|
||||||
|
|
||||||
## Color functions
|
## Color functions
|
||||||
|
|
||||||
def highlight(self, color = YELLOW_C, family = True):
|
def highlight(self, color = YELLOW_C, family = True):
|
||||||
|
|
|
@ -84,6 +84,18 @@ class VMobject(Mobject):
|
||||||
)
|
)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def match_style(self, vmobject):
|
||||||
|
#TODO: Should this be smart about matching the
|
||||||
|
#style of the family members, if they happen to
|
||||||
|
#be different?
|
||||||
|
self.set_style_data(
|
||||||
|
stroke_color = vmobject.get_stroke_color(),
|
||||||
|
stroke_width = vmobject.get_stroke_width(),
|
||||||
|
fill_color = vmobject.get_fill_color(),
|
||||||
|
fill_opacity = vmobject.get_fill_opacity(),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
def fade(self, darkness = 0.5):
|
def fade(self, darkness = 0.5):
|
||||||
for submob in self.submobject_family():
|
for submob in self.submobject_family():
|
||||||
submob.set_stroke(
|
submob.set_stroke(
|
||||||
|
|
|
@ -187,7 +187,6 @@ class PatreonEndScreen(PatreonThanks):
|
||||||
run_time = self.run_time,
|
run_time = self.run_time,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ExternallyAnimatedScene(Scene):
|
class ExternallyAnimatedScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
raise Exception("Don't actually run this class.")
|
raise Exception("Don't actually run this class.")
|
||||||
|
|
Loading…
Add table
Reference in a new issue