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.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)
|
|
|
|
)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-11 18:13:50 -08:00
|
|
|
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)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait(2)
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
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
|
|
|
|
]
|
|
|
|
)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
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
|
|
|
|
]
|
|
|
|
))
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
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))
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-13 15:51:25 -08:00
|
|
|
self.play(MoveToTarget(sum_lines, path_arc = np.pi/4))
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait(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
|
|
|
|
)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
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
|
2018-01-15 15:30:15 -08:00
|
|
|
new_sum_graph = self.axes.get_graph(
|
|
|
|
new_sum_func,
|
|
|
|
num_graph_points = 200
|
|
|
|
)
|
2018-01-14 19:27:22 -08:00
|
|
|
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),
|
|
|
|
]
|
|
|
|
])
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
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-15 15:30:15 -08:00
|
|
|
class BreakApartSum(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"frequencies" : [0.5, 1.5, 2, 2.5, 5],
|
|
|
|
"equilibrium_height" : 2.0,
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.show_initial_sound()
|
|
|
|
self.decompose_sound()
|
|
|
|
self.ponder_question()
|
|
|
|
|
|
|
|
def show_initial_sound(self):
|
|
|
|
def func(x):
|
|
|
|
return self.equilibrium_height + 0.2*np.sum([
|
|
|
|
np.cos(2*np.pi*f*x)
|
|
|
|
for f in self.frequencies
|
|
|
|
])
|
|
|
|
axes = Axes(
|
|
|
|
x_min = 0, x_max = 5,
|
|
|
|
y_min = -1, y_max = 5,
|
|
|
|
x_axis_config = {
|
|
|
|
"include_tip" : False,
|
|
|
|
"unit_size" : 2.0,
|
|
|
|
},
|
|
|
|
y_axis_config = {
|
|
|
|
"include_tip" : False,
|
|
|
|
"unit_size" : 0.5,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
axes.stretch_to_fit_width(2*SPACE_WIDTH - 2)
|
|
|
|
axes.stretch_to_fit_height(3)
|
|
|
|
axes.center()
|
|
|
|
axes.to_edge(LEFT)
|
|
|
|
graph = axes.get_graph(func, num_graph_points = 200)
|
|
|
|
graph.highlight(YELLOW)
|
|
|
|
|
|
|
|
v_line = Line(ORIGIN, 4*UP)
|
|
|
|
v_line.move_to(axes.coords_to_point(0, 0), DOWN)
|
|
|
|
dot = Dot(color = PINK)
|
|
|
|
dot.move_to(graph.point_from_proportion(0))
|
|
|
|
|
|
|
|
self.add(axes, graph)
|
|
|
|
self.play(
|
|
|
|
v_line.move_to, axes.coords_to_point(5, 0), DOWN,
|
|
|
|
MoveAlongPath(dot, graph),
|
|
|
|
run_time = 8,
|
|
|
|
rate_func = None,
|
|
|
|
)
|
|
|
|
self.play(*map(FadeOut, [dot, v_line]))
|
|
|
|
|
|
|
|
self.set_variables_as_attrs(axes, graph)
|
|
|
|
|
|
|
|
def decompose_sound(self):
|
|
|
|
axes, graph = self.axes, self.graph
|
|
|
|
|
|
|
|
pure_graphs = VGroup(*[
|
|
|
|
axes.get_graph(
|
|
|
|
lambda x : 0.5*np.cos(2*np.pi*freq*x),
|
|
|
|
num_graph_points = 100,
|
|
|
|
)
|
|
|
|
for freq in self.frequencies
|
|
|
|
])
|
|
|
|
pure_graphs.gradient_highlight(BLUE, RED)
|
|
|
|
pure_graphs.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF)
|
|
|
|
h_line = DashedLine(6*LEFT, 6*RIGHT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(axes),
|
|
|
|
graph.to_edge, UP
|
|
|
|
)
|
|
|
|
pure_graphs.next_to(graph, DOWN, LARGE_BUFF)
|
|
|
|
h_line.next_to(graph, DOWN, MED_LARGE_BUFF)
|
|
|
|
self.play(ShowCreation(h_line))
|
|
|
|
for pure_graph in reversed(pure_graphs):
|
|
|
|
self.play(ReplacementTransform(graph.copy(), pure_graph))
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-15 15:30:15 -08:00
|
|
|
|
|
|
|
self.all_graphs = VGroup(graph, h_line, pure_graphs)
|
|
|
|
self.pure_graphs = pure_graphs
|
|
|
|
|
|
|
|
def ponder_question(self):
|
|
|
|
all_graphs = self.all_graphs
|
|
|
|
pure_graphs = self.pure_graphs
|
|
|
|
randy = Randolph()
|
|
|
|
randy.to_corner(DOWN+LEFT)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 15:30:15 -08:00
|
|
|
self.play(
|
|
|
|
FadeIn(randy),
|
|
|
|
all_graphs.scale, 0.75,
|
|
|
|
all_graphs.to_corner, UP+RIGHT,
|
|
|
|
)
|
|
|
|
self.play(randy.change, "pondering", all_graphs)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
rect = SurroundingRectangle(pure_graphs, color = WHITE)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(rect),
|
|
|
|
LaggedStart(
|
|
|
|
ApplyFunction, pure_graphs,
|
|
|
|
lambda g : (lambda m : m.shift(SMALL_BUFF*UP).highlight(YELLOW), g),
|
|
|
|
rate_func = wiggle
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.play(FadeOut(rect))
|
|
|
|
self.play(Blink(randy))
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 15:30:15 -08:00
|
|
|
class Quadrant(Mobject1D):
|
|
|
|
CONFIG = {
|
|
|
|
"radius" : 2,
|
|
|
|
"stroke_width": 2,
|
2018-01-15 18:49:58 -08:00
|
|
|
"density" : 45,
|
2018-01-15 15:30:15 -08:00
|
|
|
}
|
|
|
|
def generate_points(self):
|
|
|
|
self.add_points([
|
|
|
|
r*(np.cos(theta)*RIGHT + np.sin(theta)*UP)
|
|
|
|
for r in np.arange(0, self.radius, self.epsilon)
|
|
|
|
for theta in np.arange(0, np.pi/2, self.epsilon/r)
|
|
|
|
])
|
|
|
|
|
|
|
|
class UnmixMixedPaint(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"colors" : [BLUE, RED, YELLOW, GREEN],
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
angles = np.arange(4)*np.pi/2
|
|
|
|
quadrants = PMobject(*[
|
|
|
|
Quadrant().rotate(angle).highlight(color)
|
|
|
|
for color, angle in zip(self.colors, angles)
|
|
|
|
])
|
|
|
|
quadrants.ingest_submobjects()
|
|
|
|
quadrants.sort_points(lambda p : np.random.random())
|
|
|
|
|
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
mud_color = average_color(*self.colors)
|
2018-01-15 15:30:15 -08:00
|
|
|
background_circle = Circle(
|
|
|
|
radius = 2,
|
|
|
|
stroke_width = 0,
|
|
|
|
fill_color = mud_color,
|
|
|
|
fill_opacity = 1
|
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
# start_rgbas = np.array(quadrants.rgbas)
|
|
|
|
# target_rgbas = np.zeros(start_rgbas.shape)
|
|
|
|
# target_rgbas[:,:] = color_to_rgba(mud_color)
|
|
|
|
# def update_color(quadrants, alpha):
|
|
|
|
# quadrants.rgbas = interpolate(start_rgbas, target_rgbas, alpha)
|
|
|
|
|
|
|
|
dot = Dot()
|
|
|
|
permutation = range(len(quadrants.points))
|
|
|
|
random.shuffle(permutation)
|
|
|
|
def update_quadrants(quadrants, alpha):
|
|
|
|
points = quadrants.points
|
|
|
|
points += dot.get_center()
|
|
|
|
points[:,0] -= points[:,1]
|
|
|
|
points -= dot.get_center()
|
|
|
|
|
|
|
|
quadrants.points = interpolate(
|
|
|
|
quadrants.points, quadrants.points[permutation],
|
|
|
|
0.01,
|
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
|
|
|
|
self.add(quadrants)
|
2018-01-15 15:30:15 -08:00
|
|
|
self.play(
|
2018-01-15 18:49:58 -08:00
|
|
|
# PhaseFlow(
|
|
|
|
# lambda p : rotate_vector(p, np.pi/2+0.01)/(max(np.linalg.norm(p), 0.001)),
|
|
|
|
# quadrants,
|
|
|
|
# virtual_time = 20,
|
|
|
|
# ),
|
|
|
|
# UpdateFromAlphaFunc(quadrants, update_color),
|
|
|
|
UpdateFromAlphaFunc(quadrants, update_quadrants),
|
|
|
|
run_time = 3,
|
2018-01-15 15:30:15 -08:00
|
|
|
)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
#Incomplete, and probably not useful
|
|
|
|
class MachineThatTreatsOneFrequencyDifferently(Scene):
|
|
|
|
def construct(self):
|
|
|
|
graph = self.get_cosine_graph(0.5)
|
|
|
|
frequency_mob = DecimalNumber(220, num_decimal_points = 0)
|
|
|
|
frequency_mob.next_to(graph, UP, buff = MED_LARGE_BUFF)
|
|
|
|
|
|
|
|
self.graph = graph
|
|
|
|
self.frequency_mob = frequency_mob
|
|
|
|
self.add(graph, frequency_mob)
|
2018-01-11 18:13:50 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
arrow1, q_marks, arrow2 = group = VGroup(
|
|
|
|
Vector(DOWN), TextMobject("???").scale(1.5), Vector(DOWN)
|
|
|
|
)
|
|
|
|
group.highlight(WHITE)
|
|
|
|
group.arrange_submobjects(DOWN)
|
|
|
|
group.next_to(graph, DOWN)
|
|
|
|
self.add(group)
|
2018-01-13 15:51:25 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
self.change_graph_frequency(1)
|
|
|
|
graph.highlight(GREEN)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-15 18:49:58 -08:00
|
|
|
graph.highlight(YELLOW)
|
|
|
|
self.change_graph_frequency(2)
|
2018-01-15 19:15:05 -08:00
|
|
|
self.wait()
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
def change_graph_frequency(self, frequency, run_time = 2):
|
|
|
|
graph = self.graph
|
|
|
|
frequency_mob = self.frequency_mob
|
|
|
|
curr_frequency = graph.frequency
|
|
|
|
self.play(
|
|
|
|
UpdateFromAlphaFunc(
|
|
|
|
graph, self.get_signal_update_func(graph, frequency),
|
|
|
|
),
|
|
|
|
ChangingDecimal(
|
|
|
|
frequency_mob,
|
|
|
|
lambda a : 440*interpolate(curr_frequency, frequency, a)
|
|
|
|
),
|
|
|
|
run_time = run_time,
|
|
|
|
)
|
|
|
|
graph.frequency = frequency
|
|
|
|
|
|
|
|
def get_signal_update_func(self, graph, target_frequency):
|
|
|
|
curr_frequency = graph.frequency
|
|
|
|
def update(graph, alpha):
|
|
|
|
frequency = interpolate(curr_frequency, target_frequency, alpha)
|
|
|
|
new_graph = self.get_cosine_graph(frequency)
|
|
|
|
Transform(graph, new_graph).update(1)
|
|
|
|
return graph
|
|
|
|
return update
|
|
|
|
|
|
|
|
def get_cosine_graph(self, frequency, num_steps = 200, color = YELLOW):
|
|
|
|
result = FunctionGraph(
|
|
|
|
lambda x : 0.5*np.cos(2*np.pi*frequency*x),
|
|
|
|
num_steps = num_steps
|
|
|
|
)
|
|
|
|
result.frequency = frequency
|
|
|
|
result.shift(2*UP)
|
|
|
|
return result
|
2018-01-13 15:51:25 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
class FourierMachineScene(Scene):
|
|
|
|
CONFIG = {
|
2018-01-13 15:51:25 -08:00
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
}
|
|
|
|
def get_time_graph_axes(self):
|
|
|
|
pass
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
|
2018-01-15 18:49:58 -08:00
|
|
|
class FourierMachineSceneTest(FourierMachineScene):
|
|
|
|
def construct(self):
|
|
|
|
pass
|
2018-01-13 15:51:25 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|