3b1b-manim/active_projects/fourier.py

404 lines
11 KiB
Python
Raw Normal View History

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 *
#revert_to_original_skipping_status
2018-01-11 18:13:50 -08:00
class AddingPureFrequencies(PiCreatureScene):
CONFIG = {
"A_frequency" : 2.1,
"A_color" : YELLOW,
"D_color" : PINK,
"sum_color" : GREEN,
"equilibrium_height" : 1.5,
}
2018-01-11 18:13:50 -08:00
def construct(self):
self.force_skipping()
self.add_speaker()
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()
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
def play_A440(self):
2018-01-11 18:13:50 -08:00
randy = self.pi_creature
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(
FadeIn(A_label),
2018-01-11 18:13:50 -08:00
randy.change, "pondering"
)
self.dither()
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(
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)
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
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")
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)
n = 10
2018-01-11 18:13:50 -08:00
brace = Brace(Line(
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)
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(
randy.change, "erm", graph,
2018-01-11 18:13:50 -08:00
ShowCreation(graph, run_time = 4, rate_func = None),
ShowCreation(equilibrium_line),
2018-01-11 18:13:50 -08:00
)
axes.add(equilibrium_line)
2018-01-11 18:13:50 -08:00
self.play(
GrowFromCenter(brace),
Write(words)
)
self.dither(2)
graph.save_state()
self.play(
FadeOut(brace),
FadeOut(words),
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
)
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):
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):
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):
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
l_rect, r_rect = rects = VGroup(*[
FullScreenFadeRectangle().next_to(x_point, vect, SMALL_BUFF)
for vect in LEFT, RIGHT
])
rects.save_state()
for rect, vect in zip(rects, [LEFT, RIGHT]):
rect.next_to(SPACE_WIDTH*vect, vect)
self.play(rects.restore)
self.play(*map(ShowCreation, lines))
self.dither()
self.play(MoveToTarget(sum_lines, path_arc = np.pi/4))
self.dither(2)
self.play(
l_rect.next_to, SPACE_WIDTH*LEFT, LEFT,
r_rect.next_to, SPACE_WIDTH*RIGHT, RIGHT,
*[
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),
]
]
)
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
graph = axes.get_graph(new_func)
graph.highlight(self.sum_color)
graph_copy = graph.deepcopy()
all_v_lines = VGroup(
self.get_A_graph_v_line(0),
self.get_D_graph_v_line(0),
self.get_graph_v_line(0, axes, graph)
)
x_max = axes.x_max
def update_all_v_lines(v_lines, alpha):
A_line, D_line, sum_line = v_lines
x = alpha*x_max
Transform(A_line, self.get_A_graph_v_line(x)).update(1)
Transform(D_line, self.get_D_graph_v_line(x)).update(1)
Transform(
sum_line,
self.get_graph_v_line(x, axes, graph_copy)
).update(1)
return v_lines
self.play(
UpdateFromAlphaFunc(all_v_lines, update_all_v_lines),
ShowCreation(graph),
run_time = 15,
rate_func = None
)
self.dither()
2018-01-11 18:13:50 -08:00
def add_more_notes(self):
axes = self.axes
A_label = self.A_label
D_label = self.D_label
A_group = VGroup(self.A_axes, self.A_graph)
D_group = VGroup(self.D_axes, self.D_graph)
2018-01-11 18:13:50 -08:00
####
def broadcast(self, *added_anims, **kwargs):
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)
kwargs["n_circles"] = kwargs.get("n_circles", 10)
return Broadcast(self.speaker[1], **kwargs)
2018-01-11 18:13:50 -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):
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)
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)