2018-01-11 18:13:50 -08:00
|
|
|
from helpers import *
|
|
|
|
|
|
|
|
|
|
|
|
from animation.animation import Animation
|
|
|
|
from animation.transform import *
|
|
|
|
from animation.simple_animations import *
|
|
|
|
from animation.playground import *
|
|
|
|
from animation.continual_animation import *
|
|
|
|
from topics.geometry import *
|
|
|
|
from topics.characters import *
|
|
|
|
from topics.functions import *
|
|
|
|
from topics.fractals import *
|
|
|
|
from topics.number_line import *
|
|
|
|
from topics.combinatorics import *
|
|
|
|
from topics.numerals import *
|
|
|
|
from topics.three_dimensions import *
|
|
|
|
from topics.objects import *
|
|
|
|
from topics.probability import *
|
|
|
|
from topics.complex_numbers import *
|
|
|
|
from scene import Scene
|
|
|
|
from scene.reconfigurable_scene import ReconfigurableScene
|
|
|
|
from scene.zoomed_scene import *
|
|
|
|
from camera import Camera
|
|
|
|
from mobject import *
|
|
|
|
from mobject.image_mobject import *
|
|
|
|
from mobject.vectorized_mobject import *
|
|
|
|
from mobject.svg_mobject import *
|
|
|
|
from mobject.tex_mobject import *
|
|
|
|
from topics.graph_scene import *
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
#revert_to_original_skipping_status
|
|
|
|
|
2018-01-11 18:13:50 -08:00
|
|
|
class AddingPureFrequencies(PiCreatureScene):
|
2018-01-13 15:51:25 -08:00
|
|
|
CONFIG = {
|
|
|
|
"A_frequency" : 2.1,
|
|
|
|
"A_color" : YELLOW,
|
|
|
|
"D_color" : PINK,
|
2018-01-14 19:27:22 -08:00
|
|
|
"F_color" : TEAL,
|
|
|
|
"C_color" : RED,
|
2018-01-13 15:51:25 -08:00
|
|
|
"sum_color" : GREEN,
|
|
|
|
"equilibrium_height" : 1.5,
|
|
|
|
}
|
2018-01-11 18:13:50 -08:00
|
|
|
def construct(self):
|
|
|
|
self.add_speaker()
|
2018-01-13 15:51:25 -08:00
|
|
|
self.play_A440()
|
2018-01-11 18:13:50 -08:00
|
|
|
self.measure_air_pressure()
|
|
|
|
self.play_lower_pitch()
|
|
|
|
self.play_mix()
|
|
|
|
self.separate_out_parts()
|
2018-01-13 15:51:25 -08:00
|
|
|
self.draw_sum_at_single_point()
|
|
|
|
self.draw_full_sum()
|
2018-01-11 18:13:50 -08:00
|
|
|
self.add_more_notes()
|
|
|
|
|
|
|
|
def add_speaker(self):
|
|
|
|
speaker = SVGMobject(file_name = "speaker")
|
|
|
|
speaker.to_edge(DOWN)
|
|
|
|
|
|
|
|
self.add(speaker)
|
|
|
|
self.speaker = speaker
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
def play_A440(self):
|
2018-01-11 18:13:50 -08:00
|
|
|
randy = self.pi_creature
|
2018-01-13 15:51:25 -08:00
|
|
|
A_label = TextMobject("A440")
|
|
|
|
A_label.highlight(self.A_color)
|
|
|
|
A_label.next_to(self.speaker, UP)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
self.broadcast(
|
2018-01-13 15:51:25 -08:00
|
|
|
FadeIn(A_label),
|
2018-01-14 19:27:22 -08:00
|
|
|
Succession(
|
|
|
|
ApplyMethod, randy.change, "pondering",
|
|
|
|
Animation, randy,
|
|
|
|
Blink, randy
|
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
)
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
self.set_variables_as_attrs(A_label)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
def measure_air_pressure(self):
|
|
|
|
randy = self.pi_creature
|
|
|
|
axes = Axes(
|
2018-01-13 15:51:25 -08:00
|
|
|
y_min = -2, y_max = 2,
|
2018-01-11 18:13:50 -08:00
|
|
|
x_min = 0, x_max = 10,
|
|
|
|
number_line_config = {"include_tip" : False},
|
|
|
|
)
|
|
|
|
axes.stretch_to_fit_height(2)
|
|
|
|
axes.to_corner(UP+LEFT)
|
2018-01-13 15:51:25 -08:00
|
|
|
axes.shift(LARGE_BUFF*DOWN)
|
|
|
|
eh = self.equilibrium_height
|
|
|
|
equilibrium_line = DashedLine(
|
|
|
|
axes.coords_to_point(0, eh),
|
|
|
|
axes.coords_to_point(axes.x_max, eh),
|
|
|
|
stroke_width = 2,
|
|
|
|
stroke_color = LIGHT_GREY
|
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
frequency = self.A_frequency
|
|
|
|
graph = self.get_wave_graph(frequency, axes)
|
|
|
|
func = graph.underlying_function
|
|
|
|
graph.highlight(self.A_color)
|
2018-01-11 18:13:50 -08:00
|
|
|
pressure = TextMobject("Pressure")
|
|
|
|
time = TextMobject("Time")
|
2018-01-13 15:51:25 -08:00
|
|
|
for label in pressure, time:
|
|
|
|
label.scale_in_place(0.8)
|
|
|
|
pressure.next_to(axes.y_axis, UP)
|
|
|
|
pressure.to_edge(LEFT, buff = MED_SMALL_BUFF)
|
2018-01-11 18:13:50 -08:00
|
|
|
time.next_to(axes.x_axis.get_right(), DOWN+LEFT)
|
|
|
|
axes.labels = VGroup(pressure, time)
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
n = 10
|
2018-01-11 18:13:50 -08:00
|
|
|
brace = Brace(Line(
|
2018-01-13 15:51:25 -08:00
|
|
|
axes.coords_to_point(n/frequency, func(n/frequency)),
|
|
|
|
axes.coords_to_point((n+1)/frequency, func((n+1)/frequency)),
|
2018-01-11 18:13:50 -08:00
|
|
|
), UP)
|
|
|
|
words = brace.get_text("Imagine 440 per second", buff = SMALL_BUFF)
|
2018-01-13 15:51:25 -08:00
|
|
|
words.scale(0.8, about_point = words.get_bottom())
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(pressure),
|
|
|
|
ShowCreation(axes.y_axis)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Write(time),
|
|
|
|
ShowCreation(axes.x_axis)
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
self.broadcast(
|
|
|
|
ShowCreation(graph, run_time = 4, rate_func = None),
|
2018-01-13 15:51:25 -08:00
|
|
|
ShowCreation(equilibrium_line),
|
2018-01-11 18:13:50 -08:00
|
|
|
)
|
2018-01-13 15:51:25 -08:00
|
|
|
axes.add(equilibrium_line)
|
2018-01-11 18:13:50 -08:00
|
|
|
self.play(
|
2018-01-14 19:27:22 -08:00
|
|
|
randy.change, "erm", graph,
|
2018-01-11 18:13:50 -08:00
|
|
|
GrowFromCenter(brace),
|
|
|
|
Write(words)
|
|
|
|
)
|
|
|
|
self.dither(2)
|
|
|
|
graph.save_state()
|
|
|
|
self.play(
|
|
|
|
FadeOut(brace),
|
|
|
|
FadeOut(words),
|
2018-01-13 15:51:25 -08:00
|
|
|
VGroup(axes, graph, axes.labels).shift, 0.8*UP,
|
|
|
|
graph.fade, 0.85,
|
|
|
|
graph.shift, 0.8*UP,
|
2018-01-11 18:13:50 -08:00
|
|
|
)
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
graph.saved_state.move_to(graph)
|
|
|
|
self.set_variables_as_attrs(axes, A_graph = graph)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
def play_lower_pitch(self):
|
2018-01-13 15:51:25 -08:00
|
|
|
axes = self.axes
|
|
|
|
randy = self.pi_creature
|
|
|
|
|
|
|
|
frequency = self.A_frequency*(2.0/3.0)
|
|
|
|
graph = self.get_wave_graph(frequency, axes)
|
|
|
|
graph.set_color(self.D_color)
|
|
|
|
|
|
|
|
D_label = TextMobject("D294")
|
|
|
|
D_label.highlight(self.D_color)
|
|
|
|
D_label.move_to(self.A_label)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(self.A_label),
|
|
|
|
GrowFromCenter(D_label),
|
|
|
|
)
|
|
|
|
self.broadcast(
|
|
|
|
ShowCreation(graph, run_time = 4, rate_func = None),
|
|
|
|
randy.change, "happy",
|
|
|
|
n_circles = 6,
|
|
|
|
)
|
|
|
|
self.play(randy.change, "confused", graph)
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
self.set_variables_as_attrs(
|
|
|
|
D_label,
|
|
|
|
D_graph = graph
|
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
def play_mix(self):
|
2018-01-13 15:51:25 -08:00
|
|
|
self.A_graph.restore()
|
|
|
|
self.broadcast(
|
|
|
|
self.get_broadcast_animation(n_circles = 6),
|
|
|
|
self.pi_creature.change, "thinking",
|
|
|
|
*[
|
|
|
|
ShowCreation(graph, run_time = 4, rate_func = None)
|
|
|
|
for graph in self.A_graph, self.D_graph
|
|
|
|
]
|
|
|
|
)
|
|
|
|
self.dither()
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
def separate_out_parts(self):
|
2018-01-13 15:51:25 -08:00
|
|
|
axes = self.axes
|
|
|
|
speaker = self.speaker
|
|
|
|
randy = self.pi_creature
|
|
|
|
|
|
|
|
A_axes = axes.deepcopy()
|
|
|
|
A_graph = self.A_graph
|
|
|
|
A_label = self.A_label
|
|
|
|
D_axes = axes.deepcopy()
|
|
|
|
D_graph = self.D_graph
|
|
|
|
D_label = self.D_label
|
|
|
|
movers = [A_axes, A_graph, A_label, D_axes, D_graph, D_label]
|
|
|
|
for mover in movers:
|
|
|
|
mover.generate_target()
|
|
|
|
D_target_group = VGroup(D_axes.target, D_graph.target)
|
|
|
|
A_target_group = VGroup(A_axes.target, A_graph.target)
|
|
|
|
D_target_group.next_to(axes, DOWN, MED_LARGE_BUFF)
|
|
|
|
A_target_group.next_to(D_target_group, DOWN, MED_LARGE_BUFF)
|
|
|
|
A_label.fade(1)
|
|
|
|
A_label.target.next_to(A_graph.target, UP)
|
|
|
|
D_label.target.next_to(D_graph.target, UP)
|
|
|
|
|
|
|
|
self.play(*it.chain(
|
|
|
|
map(MoveToTarget, movers),
|
|
|
|
[
|
|
|
|
ApplyMethod(mob.shift, SPACE_HEIGHT*DOWN, remover = True)
|
|
|
|
for mob in randy, speaker
|
|
|
|
]
|
|
|
|
))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
self.set_variables_as_attrs(A_axes, D_axes)
|
|
|
|
|
|
|
|
def draw_sum_at_single_point(self):
|
|
|
|
axes = self.axes
|
|
|
|
A_axes = self.A_axes
|
|
|
|
D_axes = self.D_axes
|
|
|
|
A_graph = self.A_graph
|
|
|
|
D_graph = self.D_graph
|
|
|
|
|
|
|
|
x = 2.85
|
|
|
|
A_line = self.get_A_graph_v_line(x)
|
|
|
|
D_line = self.get_D_graph_v_line(x)
|
|
|
|
lines = VGroup(A_line, D_line)
|
|
|
|
sum_lines = lines.copy()
|
|
|
|
sum_lines.generate_target()
|
|
|
|
self.stack_v_lines(x, sum_lines.target)
|
|
|
|
|
|
|
|
top_axes_point = axes.coords_to_point(x, self.equilibrium_height)
|
|
|
|
x_point = np.array(top_axes_point)
|
|
|
|
x_point[1] = 0
|
2018-01-14 19:27:22 -08:00
|
|
|
v_line = Line(UP, DOWN).scale(SPACE_HEIGHT).move_to(x_point)
|
2018-01-13 15:51:25 -08:00
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.play(GrowFromCenter(v_line))
|
|
|
|
self.play(FadeOut(v_line))
|
2018-01-13 15:51:25 -08:00
|
|
|
self.play(*map(ShowCreation, lines))
|
|
|
|
self.dither()
|
|
|
|
self.play(MoveToTarget(sum_lines, path_arc = np.pi/4))
|
|
|
|
self.dither(2)
|
2018-01-14 19:27:22 -08:00
|
|
|
self.play(*[
|
|
|
|
Transform(
|
|
|
|
line,
|
|
|
|
VectorizedPoint(axes.coords_to_point(0, self.equilibrium_height)),
|
|
|
|
remover = True
|
|
|
|
)
|
|
|
|
for line, axes in [
|
|
|
|
(A_line, A_axes),
|
|
|
|
(D_line, D_axes),
|
|
|
|
(sum_lines, axes),
|
2018-01-13 15:51:25 -08:00
|
|
|
]
|
2018-01-14 19:27:22 -08:00
|
|
|
])
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
def draw_full_sum(self):
|
|
|
|
axes = self.axes
|
|
|
|
|
|
|
|
def new_func(x):
|
|
|
|
result = self.A_graph.underlying_function(x)
|
|
|
|
result += self.D_graph.underlying_function(x)
|
|
|
|
result -= self.equilibrium_height
|
|
|
|
return result
|
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
sum_graph = axes.get_graph(new_func)
|
|
|
|
sum_graph.highlight(self.sum_color)
|
2018-01-13 15:51:25 -08:00
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
##TODO
|
2018-01-13 15:51:25 -08:00
|
|
|
self.play(
|
2018-01-14 19:27:22 -08:00
|
|
|
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(axes, sum_graph.deepcopy()),
|
|
|
|
ShowCreation(sum_graph),
|
2018-01-13 15:51:25 -08:00
|
|
|
run_time = 15,
|
|
|
|
rate_func = None
|
|
|
|
)
|
|
|
|
self.dither()
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
self.sum_graph = sum_graph
|
|
|
|
|
2018-01-11 18:13:50 -08:00
|
|
|
def add_more_notes(self):
|
2018-01-13 15:51:25 -08:00
|
|
|
axes = self.axes
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
A_group = VGroup(self.A_axes, self.A_graph, self.A_label)
|
|
|
|
D_group = VGroup(self.D_axes, self.D_graph, self.D_label)
|
|
|
|
squish_group = VGroup(A_group, D_group)
|
|
|
|
squish_group.generate_target()
|
|
|
|
squish_group.target.stretch(0.5, 1)
|
|
|
|
squish_group.target.next_to(axes, DOWN, buff = -SMALL_BUFF)
|
|
|
|
for group in squish_group.target:
|
|
|
|
label = group[-1]
|
|
|
|
bottom = label.get_bottom()
|
|
|
|
label.stretch_in_place(0.5, 0)
|
|
|
|
label.move_to(bottom, DOWN)
|
|
|
|
|
|
|
|
self.play(MoveToTarget(squish_group))
|
|
|
|
|
|
|
|
F_axes = self.D_axes.deepcopy()
|
|
|
|
C_axes = self.A_axes.deepcopy()
|
|
|
|
VGroup(F_axes, C_axes).next_to(squish_group, DOWN)
|
|
|
|
F_graph = self.get_wave_graph(self.A_frequency*4.0/5, F_axes)
|
|
|
|
F_graph.highlight(self.F_color)
|
|
|
|
C_graph = self.get_wave_graph(self.A_frequency*6.0/5, C_axes)
|
|
|
|
C_graph.highlight(self.C_color)
|
|
|
|
|
|
|
|
F_label = TextMobject("F349")
|
|
|
|
C_label = TextMobject("C523")
|
|
|
|
for label, graph in (F_label, F_graph), (C_label, C_graph):
|
|
|
|
label.scale(0.5)
|
|
|
|
label.highlight(graph.get_stroke_color())
|
|
|
|
label.next_to(graph, UP, SMALL_BUFF)
|
|
|
|
|
|
|
|
graphs = [self.A_graph, self.D_graph, F_graph, C_graph]
|
|
|
|
def new_sum_func(x):
|
|
|
|
result = sum([
|
|
|
|
graph.underlying_function(x) - self.equilibrium_height
|
|
|
|
for graph in graphs
|
|
|
|
])
|
|
|
|
result *= 0.5
|
|
|
|
return result + self.equilibrium_height
|
|
|
|
new_sum_graph = self.axes.get_graph(new_sum_func)
|
|
|
|
new_sum_graph.highlight(BLUE_C)
|
|
|
|
|
|
|
|
self.play(*it.chain(
|
|
|
|
map(ShowCreation, [F_axes, C_axes, F_graph, C_graph,]),
|
|
|
|
map(Write, [F_label, C_label]),
|
|
|
|
[FadeOut(self.sum_graph)]
|
|
|
|
))
|
|
|
|
kwargs = {"rate_func" : None, "run_time" : 10}
|
|
|
|
self.play(ShowCreation(new_sum_graph.copy(), **kwargs), *[
|
|
|
|
self.get_graph_line_animation(curr_axes, graph, **kwargs)
|
|
|
|
for curr_axes, graph in [
|
|
|
|
(self.A_axes, self.A_graph),
|
|
|
|
(self.D_axes, self.D_graph),
|
|
|
|
(F_axes, F_graph),
|
|
|
|
(C_axes, C_graph),
|
|
|
|
(axes, new_sum_graph),
|
|
|
|
]
|
|
|
|
])
|
|
|
|
self.dither()
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
####
|
|
|
|
|
|
|
|
def broadcast(self, *added_anims, **kwargs):
|
2018-01-13 15:51:25 -08:00
|
|
|
self.play(self.get_broadcast_animation(**kwargs), *added_anims)
|
|
|
|
|
|
|
|
def get_broadcast_animation(self, **kwargs):
|
2018-01-11 18:13:50 -08:00
|
|
|
kwargs["run_time"] = kwargs.get("run_time", 5)
|
2018-01-13 15:51:25 -08:00
|
|
|
kwargs["n_circles"] = kwargs.get("n_circles", 10)
|
|
|
|
return Broadcast(self.speaker[1], **kwargs)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
def get_wave_graph(self, frequency, axes):
|
2018-01-11 18:13:50 -08:00
|
|
|
tail_len = 3.0
|
|
|
|
x_min, x_max = axes.x_min, axes.x_max
|
|
|
|
def func(x):
|
2018-01-13 15:51:25 -08:00
|
|
|
value = 0.7*np.cos(2*np.pi*frequency*x)
|
2018-01-11 18:13:50 -08:00
|
|
|
if x - x_min < tail_len:
|
|
|
|
value *= smooth((x-x_min)/tail_len)
|
|
|
|
if x_max - x < tail_len:
|
|
|
|
value *= smooth((x_max - x )/tail_len)
|
2018-01-13 15:51:25 -08:00
|
|
|
return value + self.equilibrium_height
|
|
|
|
ngp = 2*(x_max - x_min)*frequency + 1
|
|
|
|
graph = axes.get_graph(func, num_graph_points = ngp)
|
|
|
|
return graph
|
|
|
|
|
|
|
|
def get_A_graph_v_line(self, x):
|
|
|
|
return self.get_graph_v_line(x, self.A_axes, self.A_graph)
|
|
|
|
|
|
|
|
def get_D_graph_v_line(self, x):
|
|
|
|
return self.get_graph_v_line(x, self.D_axes, self.D_graph)
|
|
|
|
|
|
|
|
def get_graph_v_line(self, x, axes, graph):
|
|
|
|
result = Line(
|
|
|
|
axes.coords_to_point(x, self.equilibrium_height),
|
|
|
|
# axes.coords_to_point(x, graph.underlying_function(x)),
|
|
|
|
graph.point_from_proportion(float(x)/axes.x_max),
|
|
|
|
color = WHITE,
|
|
|
|
buff = 0,
|
|
|
|
)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def stack_v_lines(self, x, lines):
|
|
|
|
point = self.axes.coords_to_point(x, self.equilibrium_height)
|
|
|
|
A_line, D_line = lines
|
|
|
|
A_line.shift(point - A_line.get_start())
|
|
|
|
D_line.shift(A_line.get_end()-D_line.get_start())
|
|
|
|
A_line.highlight(self.A_color)
|
|
|
|
D_line.highlight(self.D_color)
|
|
|
|
return lines
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
def create_pi_creature(self):
|
|
|
|
return Randolph().to_corner(DOWN+LEFT)
|
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
def get_graph_line_animation(self, axes, graph, **kwargs):
|
|
|
|
line = self.get_graph_v_line(0, axes, graph)
|
|
|
|
x_max = axes.x_max
|
|
|
|
def update_line(line, alpha):
|
|
|
|
x = alpha*x_max
|
|
|
|
Transform(line, self.get_graph_v_line(x, axes, graph)).update(1)
|
|
|
|
return line
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-14 19:27:22 -08:00
|
|
|
return UpdateFromAlphaFunc(line, update_line, **kwargs)
|
|
|
|
|
2018-01-11 18:13:50 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|