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 class AddingPureFrequencies(PiCreatureScene): CONFIG = { "A_frequency" : 2.1, "A_color" : YELLOW, "D_color" : PINK, "F_color" : TEAL, "C_color" : RED, "sum_color" : GREEN, "equilibrium_height" : 1.5, } def construct(self): self.add_speaker() self.play_A440() 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() 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): randy = self.pi_creature A_label = TextMobject("A440") A_label.highlight(self.A_color) A_label.next_to(self.speaker, UP) self.broadcast( FadeIn(A_label), Succession( ApplyMethod, randy.change, "pondering", Animation, randy, Blink, randy ) ) self.set_variables_as_attrs(A_label) def measure_air_pressure(self): randy = self.pi_creature axes = Axes( y_min = -2, y_max = 2, 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 ) frequency = self.A_frequency graph = self.get_wave_graph(frequency, axes) func = graph.underlying_function graph.highlight(self.A_color) 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) time.next_to(axes.x_axis.get_right(), DOWN+LEFT) axes.labels = VGroup(pressure, time) n = 10 brace = Brace(Line( axes.coords_to_point(n/frequency, func(n/frequency)), axes.coords_to_point((n+1)/frequency, func((n+1)/frequency)), ), UP) words = brace.get_text("Imagine 440 per second", buff = SMALL_BUFF) words.scale(0.8, about_point = words.get_bottom()) 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), ShowCreation(equilibrium_line), ) axes.add(equilibrium_line) self.play( randy.change, "erm", graph, 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, ) graph.saved_state.move_to(graph) self.set_variables_as_attrs(axes, A_graph = graph) 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 ) 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() 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 v_line = Line(UP, DOWN).scale(SPACE_HEIGHT).move_to(x_point) self.revert_to_original_skipping_status() self.play(GrowFromCenter(v_line)) self.play(FadeOut(v_line)) self.play(*map(ShowCreation, lines)) self.dither() self.play(MoveToTarget(sum_lines, path_arc = np.pi/4)) self.dither(2) 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), ] ]) 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 sum_graph = axes.get_graph(new_func) sum_graph.highlight(self.sum_color) ##TODO self.play( 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), run_time = 15, rate_func = None ) self.dither() self.sum_graph = sum_graph def add_more_notes(self): axes = self.axes 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() #### def broadcast(self, *added_anims, **kwargs): self.play(self.get_broadcast_animation(**kwargs), *added_anims) def get_broadcast_animation(self, **kwargs): kwargs["run_time"] = kwargs.get("run_time", 5) kwargs["n_circles"] = kwargs.get("n_circles", 10) return Broadcast(self.speaker[1], **kwargs) def get_wave_graph(self, frequency, axes): 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) 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 def create_pi_creature(self): return Randolph().to_corner(DOWN+LEFT) 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 return UpdateFromAlphaFunc(line, update_line, **kwargs)