diff --git a/bell.py b/bell.py index e57c8357..afb9b416 100644 --- a/bell.py +++ b/bell.py @@ -48,10 +48,14 @@ class PhotonPassesCompletelyOrNotAtAll(DirectionOfPolarizationScene): "start_theta" : -0.9*np.pi, "target_theta" : -0.6*np.pi, "apply_filter" : True, - "lower_portion_shift" : 3*IN + "lower_portion_shift" : 3*IN, + "show_M_vects" : True, } def setup(self): DirectionOfPolarizationScene.setup(self) + if not self.show_M_vects: + for M_vect in self.em_wave.M_vects: + M_vect.set_fill(opacity = 0) self.continual_update() for vect in it.chain(self.em_wave.E_vects, self.em_wave.M_vects): vect.reset_normal_vector() @@ -64,9 +68,9 @@ class PhotonPassesCompletelyOrNotAtAll(DirectionOfPolarizationScene): lower_filter.save_state() pol_filter.remove(pol_filter.label) - passing_words = TextMobject("Photon", "passes through") + passing_words = TextMobject("Photon", "passes through\\\\", "entirely") passing_words.highlight(GREEN) - filtered_words = TextMobject("Photon", "is blocked") + filtered_words = TextMobject("Photon", "is blocked\\\\", "entirely") filtered_words.highlight(RED) for words in passing_words, filtered_words: words.next_to(ORIGIN, UP+LEFT) @@ -114,7 +118,7 @@ class PhotonPassesCompletelyOrNotAtAll(DirectionOfPolarizationScene): rate_func = squish_rate_func(there_and_back, 0.4, 0.6), run_time = filtered_photon.run_time ) - for x in range(3): + for x in range(4): self.play( passing_photon, filtered_photon, @@ -123,6 +127,11 @@ class PhotonPassesCompletelyOrNotAtAll(DirectionOfPolarizationScene): ) self.dither() +class PhotonPassesCompletelyOrNotAtAllForWavesVideo(PhotonPassesCompletelyOrNotAtAll): + CONFIG = { + "show_M_vects" : False, + } + class DirectionOfPolarization(DirectionOfPolarizationScene): def construct(self): self.remove(self.pol_filter) @@ -197,8 +206,8 @@ class PhotonsThroughPerpendicularFilters(PhotonPassesCompletelyOrNotAtAll): def shoot_photon(self, *added_anims): photon = self.get_photons()[1] pol_filter = self.pol_filters[0] - absorbtion = self.get_filter_absorbtion_animation(pol_filter, photon) - self.play(photon, absorbtion) + absorption = self.get_filter_absorption_animation(pol_filter, photon) + self.play(photon, absorption) def get_photons(self): @@ -533,7 +542,7 @@ class BasicsOfPolarization(DirectionOfPolarizationScene): [passing_photon], [ filtered_photon, - self.get_filter_absorbtion_animation( + self.get_filter_absorption_animation( self.pol_filter, filtered_photon ) @@ -663,11 +672,11 @@ class ShowVariousFilterPairsWithPhotonsOverTime(PhotonsThroughPerpendicularFilte blocked_photon.rate_func = squish_rate_func( lambda x : x, 0, 0.5, ) - first_absorbtion = self.get_filter_absorbtion_animation( + first_absorption = self.get_filter_absorption_animation( self.pol_filters[0], blocked_photon ) - first_absorbtion.rate_func = squish_rate_func( - first_absorbtion.rate_func, 0, 0.5, + first_absorption.rate_func = squish_rate_func( + first_absorption.rate_func, 0, 0.5, ) photons = [ @@ -681,16 +690,16 @@ class ShowVariousFilterPairsWithPhotonsOverTime(PhotonsThroughPerpendicularFilte ) added_anims = [] if photon.filter_distance == SPACE_WIDTH + 2: - absorbtion = self.get_filter_absorbtion_animation( + absorption = self.get_filter_absorption_animation( self.second_filter, photon ) - absorbtion.rate_func = squish_rate_func( - absorbtion.rate_func, 0.5, 1 + absorption.rate_func = squish_rate_func( + absorption.rate_func, 0.5, 1 ) - added_anims.append(absorbtion) + added_anims.append(absorption) self.play( blocked_photon, - first_absorbtion, + first_absorption, photon, *added_anims ) @@ -721,11 +730,11 @@ class ShowVariousFilterPairs(ShowVariousFilterPairsWithPhotonsOverTime): "filter_z_coordinates" : [2.5, 0, -2.5], "angles" : [0, np.pi/4, np.pi/2], "n_lines" : 20, - "line_start_length" : 16, - "line_end_length" : 16, "new_group_shift_val" : 2.5*IN, "prev_group_shift_val" : 1.75*IN, "ambient_rotation_rate" : 0.015, + "line_start_length" : 16, + "line_end_length" : 16, "lines_depth" : 1.2, "lines_shift_vect" : SMALL_BUFF*OUT, } diff --git a/helpers.py b/helpers.py index faf99fe3..3f505704 100644 --- a/helpers.py +++ b/helpers.py @@ -478,6 +478,14 @@ def there_and_back(t, inflection = 10.0): new_t = 2*t if t < 0.5 else 2*(1 - t) return smooth(new_t, inflection) +def there_and_back_with_pause(t): + if t < 1./3: + return smooth(3*t) + elif t < 2./3: + return 1 + else: + return smooth(3 - 3*t) + def running_start(t, pull_factor = -0.5): return bezier([0, 0, pull_factor, pull_factor, 1, 1, 1])(t) diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index 6ef91187..2ee3d5d1 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -91,7 +91,6 @@ class TexMobject(SVGMobject): len(tex) > len(t1) and tex[len(t1)] in "()[]\\" ]) if should_replace: - print len(t1) tex = tex.replace(t1, "\\big") if tex == "": tex = "\\quad" diff --git a/scene/scene.py b/scene/scene.py index bf39e26d..debc3aa8 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -108,7 +108,10 @@ class Scene(object): if "include_submobjects" not in kwargs: kwargs["include_submobjects"] = False if mobjects is None: - mobjects = self.mobjects + mobjects = list_update( + self.foreground_mobjects, + self.mobjects, + ) if background is not None: self.set_camera_image(background) else: diff --git a/waves.py b/waves.py index 6fe8d71a..dc928280 100644 --- a/waves.py +++ b/waves.py @@ -21,6 +21,7 @@ from topics.three_dimensions import * from topics.objects import * from topics.probability import * from topics.complex_numbers import * +from topics.common_scenes import * from scene import Scene from scene.reconfigurable_scene import ReconfigurableScene from scene.zoomed_scene import * @@ -28,7 +29,6 @@ from camera import Camera from mobject.svg_mobject import * from mobject.tex_mobject import * - E_COLOR = BLUE M_COLOR = YELLOW @@ -205,6 +205,7 @@ class WavePacket(Animation): "filter_distance" : SPACE_WIDTH, "get_filtered" : False, "remover" : True, + "width" : 2*np.pi, } def __init__(self, **kwargs): digest_config(self, kwargs) @@ -246,11 +247,17 @@ class WavePacket(Animation): distance_from_start = np.linalg.norm(tail - em_wave.start_point) if self.get_filtered and distance_from_start > self.filter_distance: A = 0 + epsilon = 0.05 + if abs(A) < epsilon: + A = 0 vect.restore() + if vect.get_length() < epsilon: + pass vect.scale(A/vect.get_length(), about_point = tail) def E_func(self, x): - return np.sin(x)*np.exp(-0.25*x*x) + x0 = 2*np.pi*x/self.width + return np.sin(x0)*np.exp(-0.25*x0*x0) class FilterLabel(TexMobject): def __init__(self, tex, degrees, **kwargs): @@ -333,7 +340,7 @@ class FilterScene(ThreeDScene): if self.ambient_rotation_rate > 0: self.begin_ambient_camera_rotation(self.ambient_rotation_rate) - def get_filter_absorbtion_animation(self, pol_filter, photon): + def get_filter_absorption_animation(self, pol_filter, photon): x = pol_filter.get_center()[0] alpha = (x + SPACE_WIDTH) / (2*SPACE_WIDTH) return ApplyMethod( @@ -351,6 +358,7 @@ class DirectionOfPolarizationScene(FilterScene): "target_phi" : 0.9*np.pi/2, "ambient_rotation_rate" : 0.005, "apply_filter" : False, + "quantum" : False, } def setup(self): self.reference_line = Line(ORIGIN, RIGHT) @@ -408,23 +416,21 @@ class DirectionOfPolarizationScene(FilterScene): for pol_filter in filters: filter_x = pol_filter.arrow.get_center()[0] for vect_group, angle in zip(vect_groups, [0, -np.pi/2]): - proj_vect = rotate_vector( - OUT, pol_filter.filter_angle + angle, RIGHT, - ) - proj_matrix = np.array([RIGHT] + [ - proj_vect*np.dot(proj_vect, basis) - for basis in UP, OUT - ]).T - for vect in vect_group: - start, end = vect.get_start_and_end() + target_angle = pol_filter.filter_angle + angle + for vect_mob in vect_group: + vect = vect_mob.get_vector() + vect_angle = angle_of_vector([ + vect[2], -vect[1] + ]) + angle_diff = target_angle - vect_angle + start, end = vect_mob.get_start_and_end() if start[0] > filter_x: - vect.apply_matrix(proj_matrix) - vect.shift(start - vect.get_start()) - vect.set_tip_points(vect.tip) - vect.set_rectangular_stem_points() + vect_mob.rotate(angle_diff, RIGHT) + if not self.quantum: + vect_mob.scale(np.cos(angle_diff)) def update_rectangles(self): - if self.rectangles not in self.mobjects: + if not hasattr(self, "rectangles") or self.rectangles not in self.mobjects: return r1, r2 = self.rectangles @@ -445,6 +451,43 @@ class DirectionOfPolarizationScene(FilterScene): ################ +class AskWhatsDifferentInQM(TeacherStudentsScene): + def construct(self): + self.student_says( + "What's different in \\\\ quantum mechanics?" + ) + self.play(self.teacher.change, "pondering") + self.dither(3) + +class VideoWrapper(Scene): + CONFIG = { + "title" : "" + } + def construct(self): + title = TextMobject(self.title) + title.to_edge(UP) + self.add(title) + rect = ScreenRectangle() + rect.scale_to_fit_height(6) + rect.next_to(title, DOWN) + self.add(rect) + self.dither() + +class BellsWrapper(VideoWrapper): + CONFIG = { + "title" : "Bell's inequalities" + } + +class FromOtherVideoWrapper(VideoWrapper): + CONFIG = { + "title" : "See the other video..." + } + +class OriginOfQuantumMechanicsWrapper(VideoWrapper): + CONFIG = { + "title" : "The origin of quantum mechanics" + } + class IntroduceElectricField(PiCreatureScene): CONFIG = { "vector_field_colors" : [BLUE_B, BLUE_D], @@ -2138,9 +2181,10 @@ class EnergyOfWavesWavePortion(DirectWaveOutOfScreen): self.v_wave = v_wave def scale_up_and_down(self): - for scale_factor in 1.25, 0.4, 1.5, 0.5: + for scale_factor in 1.25, 0.4, 1.5, 0.3, 2: self.scale_wave(scale_factor) self.dither() + self.dither(4) ###### @@ -2266,6 +2310,10 @@ class EnergyOfWavesTeacherPortion(TeacherStudentsScene): self.dither(5) class DescribePhoton(ThreeDScene): + CONFIG = { + "x_color" : RED, + "y_color" : GREEN, + } def setup(self): self.axes = ThreeDAxes() self.add(self.axes) @@ -2292,20 +2340,19 @@ class DescribePhoton(ThreeDScene): self.em_wave = em_wave def construct(self): - self.force_skipping() - self.add_ket_equation() self.shoot_a_few_photons() self.freeze_photon() self.reposition_to_face_photon_head_on() self.show_components() - self.change_basis() self.show_amplitude_and_phase() + self.change_basis() self.write_different_meaning() self.write_components() self.describe_via_energy() self.components_not_possible_in_isolation() self.ask_what_they_mean() + self.change_camera() def add_ket_equation(self): equation = TexMobject( @@ -2314,10 +2361,10 @@ class DescribePhoton(ThreeDScene): "\\alpha", "|\\!\\rightarrow \\rangle", "+", "\\beta", "|\\!\\uparrow \\rangle", ) - equation.to_edge(UP).shift(MED_SMALL_BUFF*RIGHT) + equation.to_edge(UP) equation.highlight_by_tex("psi", E_COLOR) - equation.highlight_by_tex("alpha", GREEN) - equation.highlight_by_tex("beta", RED) + equation.highlight_by_tex("alpha", self.x_color) + equation.highlight_by_tex("beta", self.y_color) rect = SurroundingRectangle(equation.get_part_by_tex("psi")) rect.highlight(E_COLOR) words = TextMobject("Polarization\\\\", "state") @@ -2337,6 +2384,7 @@ class DescribePhoton(ThreeDScene): self.add(equation) self.equation = equation + self.superposition = VGroup(*equation[1][2:]) def shoot_a_few_photons(self): for x in range(2): @@ -2345,7 +2393,7 @@ class DescribePhoton(ThreeDScene): def freeze_photon(self): self.play( self.photon, - rate_func = lambda x : 0.5*x, + rate_func = lambda x : 0.55*x, run_time = 1 ) self.add(self.photon.mobject) @@ -2385,7 +2433,7 @@ class DescribePhoton(ThreeDScene): color = color, normal_vector = RIGHT, ) - for color, direction in (GREEN, UP), (RED, OUT) + for color, direction in (self.x_color, UP), (self.y_color, OUT) ] v_arrow.move_to(h_arrow.get_end(), IN) h_part = VGroup(*self.equation[1][2:4]).copy() @@ -2418,7 +2466,7 @@ class DescribePhoton(ThreeDScene): new_alpha = alpha.copy().shift(IN) rhs = TexMobject( "=", "A_x", "e", - "^{2\\pi", "f", "t", "+", "\\phi_x }" + "^{i", "(2\\pi", "f", "t", "+", "\\phi_x)}" ) A_rect = SurroundingRectangle(rhs.get_part_by_tex("A_x"), buff = 0.5*SMALL_BUFF) A_word = TextMobject("Amplitude") @@ -2426,7 +2474,7 @@ class DescribePhoton(ThreeDScene): A_word.next_to(A_rect, DOWN, aligned_edge = LEFT) A_group = VGroup(A_rect, A_word) A_group.highlight(YELLOW) - phase_rect = SurroundingRectangle(VGroup(*rhs[3:]), buff = 0.5*SMALL_BUFF) + phase_rect = SurroundingRectangle(VGroup(*rhs[4:]), buff = 0.5*SMALL_BUFF) phase_word = TextMobject("Phase") phase_word.add_background_rectangle() phase_word.next_to(phase_rect, UP) @@ -2452,7 +2500,7 @@ class DescribePhoton(ThreeDScene): self.play(*map(FadeOut, [new_alpha, group])) def change_basis(self): - superposition = VGroup(*self.equation[1][2:]) + superposition = self.superposition plane = self.xy_plane h_arrow = self.h_arrow v_arrow = self.v_arrow @@ -2513,40 +2561,732 @@ class DescribePhoton(ThreeDScene): self.equation.generate_target() - self.revert_to_original_skipping_status() self.play(*map(MoveToTarget, movers)) self.dither(2) self.play(*[mob.restore for mob in movers]) self.dither() def write_different_meaning(self): - pass + superposition = self.superposition + superposition.rotate(np.pi/2, DOWN) + rect = SurroundingRectangle(superposition) + VGroup(superposition, rect).rotate(np.pi/2, UP) + morty = Mortimer(mode = "confused") + blinked = morty.copy().blink() + words = TextMobject("Means something \\\\ different...") + for mob in morty, blinked, words: + mob.rotate(np.pi/2, RIGHT) + mob.rotate(np.pi/2, OUT) + words.next_to(rect, UP) + VGroup(morty, blinked).next_to(words, IN) + + self.play( + ShowCreation(rect), + Write(words, run_time = 2) + ) + self.play(FadeIn(morty)) + self.play(Transform( + morty, blinked, + rate_func = squish_rate_func(there_and_back) + )) + self.dither() + self.play(*map(FadeOut, [ + morty, words, rect, + self.equation.rect, + self.equation.words, + ])) def write_components(self): - pass + d_brace = Brace(Line(ORIGIN, 2*RIGHT), UP, buff = SMALL_BUFF) + h_brace = Brace(Line(ORIGIN, (2/np.sqrt(2))*RIGHT), DOWN, buff = SMALL_BUFF) + v_brace = Brace(Line(ORIGIN, (2/np.sqrt(2))*UP), RIGHT, buff = SMALL_BUFF) + d_brace.rotate(np.pi/4) + v_brace.shift((2/np.sqrt(2))*RIGHT) + braces = VGroup(d_brace, h_brace, v_brace) + group = VGroup(braces) + + tex = ["1"] + 2*["\\sqrt{1/2}"] + colors = BLUE, self.x_color, self.y_color + for brace, tex, color in zip(braces, tex, colors): + brace.label = brace.get_tex(tex, buff = SMALL_BUFF) + brace.label.add_background_rectangle() + brace.label.highlight(color) + group.add(brace.label) + + group.rotate(np.pi/2, RIGHT) + group.rotate(np.pi/2, OUT) + + self.play( + GrowFromCenter(d_brace), + Write(d_brace.label) + ) + self.dither() + self.play( + FadeOut(self.h_part_tex), + FadeOut(self.v_part_tex), + GrowFromCenter(h_brace), + GrowFromCenter(v_brace), + ) + self.play( + Write(h_brace.label), + Write(v_brace.label), + ) + self.dither() + + self.d_brace = d_brace + self.h_brace = h_brace + self.v_brace = v_brace def describe_via_energy(self): - pass + energy = TexMobject( + "&\\text{Energy}", + "=", "(hf)", "(", "1", ")^2\\\\", + "&=", "(hf)", "\\left(", "\\sqrt{1/2}", "\\right)^2", + "+", "(hf)", "\\left(", "\\sqrt{1/2}", "\\right)^2", + ) + energy.scale(0.8) + one = energy.get_part_by_tex("1", substring = False) + one.highlight(BLUE) + halves = energy.get_parts_by_tex("1/2") + halves[0].highlight(self.x_color) + halves[1].highlight(self.y_color) + indices = [0, 3, 6, len(energy)] + parts = VGroup(*[ + VGroup(*energy[i1:i2]) + for i1, i2 in zip(indices, indices[1:]) + ]) + for part in parts: + bg_rect = BackgroundRectangle(part) + bg_rect.stretch_in_place(1.5, 1) + part.add_to_back(bg_rect) + + parts.to_corner(UP+LEFT, buff = MED_SMALL_BUFF) + parts.shift(DOWN) + parts.rotate(np.pi/2, RIGHT) + parts.rotate(np.pi/2, OUT) + + self.play(Write(parts[0]), run_time = 2) + self.play(Indicate(energy.get_part_by_tex("hf"))) + self.play( + Transform( + self.d_brace.label.copy(), + one.copy(), + remover = True + ), + Write(parts[1], run_time = 1), + ) + self.dither() + self.play( + Transform( + self.h_brace.label[1].copy(), + halves[0].copy(), + remover = True, + rate_func = squish_rate_func(smooth, 0, 0.75) + ), + Transform( + self.v_brace.label[1].copy(), + halves[1].copy(), + remover = True, + rate_func = squish_rate_func(smooth, 0.25, 1) + ), + Write(parts[2]), + run_time = 2 + ) + self.dither() + + self.energy_equation_parts = parts def components_not_possible_in_isolation(self): - pass + half_hf = VGroup(*self.energy_equation_parts[2][1:6]) + half_hf.rotate(np.pi/2, DOWN) + rect = SurroundingRectangle(half_hf) + VGroup(half_hf, rect).rotate(np.pi/2, UP) + + randy = Randolph() + randy.scale(0.7) + randy.look(UP) + randy.rotate(np.pi/2, RIGHT) + randy.rotate(np.pi/2, OUT) + randy.next_to(rect, IN) + + self.play( + ShowCreation(rect), + FadeIn(randy) + ) + self.play( + randy.rotate, np.pi/2, IN, + randy.rotate, np.pi/2, LEFT, + randy.change, "maybe", + randy.rotate, np.pi/2, RIGHT, + randy.rotate, np.pi/2, OUT, + ) + self.dither() def ask_what_they_mean(self): - pass + morty = Mortimer(mode = "confused") + morty.scale(0.7) + morty.to_edge(LEFT) + bubble = morty.get_bubble() + bubble.write("?!?") + bubble.resize_to_content() + bubble.add(bubble.content) + bubble.pin_to(morty) -class SuperpositionHasDifferentInterpretation(TeacherStudentsScene): + group = VGroup(morty, bubble) + group.to_corner(DOWN+RIGHT) + group.rotate(np.pi/2, RIGHT) + group.rotate(np.pi/2, OUT) + + component = VGroup(self.h_arrow, self.h_brace, self.h_brace.label) + + self.play( + FadeIn(morty), + component.next_to, morty, DOWN, OUT, + component.shift, MED_LARGE_BUFF*(DOWN + OUT), + ) + component.rotate(np.pi/2, DOWN) + cross = Cross(component) + VGroup(component, cross).rotate(np.pi/2, UP) + cross.highlight("#ff0000") + self.play(ShowCreation(cross)) + bubble.remove(bubble.content) + self.play( + ShowCreation(bubble), + Write(bubble.content), + morty.look_at, component, + ) + self.dither() + + def change_camera(self): + everything = VGroup(*self.get_top_level_mobjects()) + everything.remove(self.photon.mobject) + everything.remove(self.axes) + + self.play(*map(FadeOut, everything)) + self.move_camera( + phi = 0.8*np.pi/2, + theta = -0.3*np.pi, + run_time = 2 + ) + self.play( + self.photon, + rate_func = lambda x : min(x + 0.55, 1), + run_time = 2, + ) + self.photon.rate_func = lambda x : x + self.play(self.photon) + self.dither() + +class GetExperimental(TeacherStudentsScene): def construct(self): - pass - - - - - - - - - + self.teacher_says("Get experimental!", target_mode = "hooray") + self.change_student_modes(*["hooray"]*3) + self.dither(3) + +class ShootPhotonThroughFilter(DirectionOfPolarizationScene): + CONFIG = { + "EMWave_config" : { + "wave_number" : 0, + "A_vect" : [0, 1, 1], + "start_point" : SPACE_WIDTH*LEFT, + "amplitude" : np.sqrt(2), + }, + "pol_filter_configs" : [{ + "label_tex" : "\\text{Filter}", + "include_arrow_label" : False, + }], + "apply_filter" : True, + "quantum" : True, + "pre_filter_alpha" : 0.35, + "ambient_rotation_rate" : 0, + } + def setup(self): + DirectionOfPolarizationScene.setup(self) + self.em_wave.update(0) + self.remove(self.em_wave) + + def construct(self): + self.add_superposition_tex() + self.ask_what_would_happen() + self.expect_half_energy_to_be_absorbed() + self.probabalistic_passing_and_blocking() + self.note_change_in_polarization() + + def add_superposition_tex(self): + superposition_tex = TexMobject( + "|\\!\\nearrow\\rangle", + "=", + "(\\sqrt{1/2})", "|\\!\\rightarrow \\rangle", "+", + "(\\sqrt{1/2})", "|\\!\\uparrow \\rangle", + ) + superposition_tex.scale(0.9) + superposition_tex[0].highlight(E_COLOR) + halves = superposition_tex.get_parts_by_tex("1/2") + for half, color in zip(halves, [RED, GREEN]): + half.highlight(color) + + h_rect = SurroundingRectangle(VGroup(*superposition_tex[2:4])) + v_rect = SurroundingRectangle(VGroup(*superposition_tex[5:7])) + VGroup(h_rect, v_rect).fade(1) + superposition_tex.h_rect = h_rect + superposition_tex.v_rect = v_rect + superposition_tex.add(h_rect, v_rect) + + superposition_tex.next_to(ORIGIN, LEFT) + superposition_tex.to_edge(UP) + superposition_tex.rotate(np.pi/2, RIGHT) + self.superposition_tex = superposition_tex + + def ask_what_would_happen(self): + photon = self.get_photon( + rate_func = lambda t : self.pre_filter_alpha*t, + remover = False, + run_time = 0.6, + ) + question = TextMobject("What's going to happen?") + question.add_background_rectangle() + question.highlight(YELLOW) + question.rotate(np.pi/2, RIGHT) + question.next_to(self.superposition_tex, IN) + + self.pol_filter.add( + self.pol_filter.arrow.copy().rotate(np.pi/2, OUT) + ) + self.pol_filter.save_state() + self.pol_filter.shift(5*OUT) + + self.set_camera_position(theta = -0.9*np.pi) + self.play(self.pol_filter.restore) + self.move_camera( + theta = -0.6*np.pi, + ) + self.play( + photon, + FadeIn(self.superposition_tex) + ) + self.play(Write(question, run_time = 1)) + self.dither() + self.play(FadeOut(self.pol_filter.label)) + self.pol_filter.remove(self.pol_filter.label) + self.add(self.pol_filter) + + self.question = question + self.frozen_photon = photon + + def expect_half_energy_to_be_absorbed(self): + words = TextMobject("Absorbs horizontal \\\\ energy") + words.highlight(RED) + words.next_to(ORIGIN, UP+RIGHT, MED_LARGE_BUFF) + words.rotate(np.pi/2, RIGHT) + words.rotate(np.pi/2, OUT) + + lines = VGroup(*[ + Line( + np.sin(a)*RIGHT + np.cos(a)*UP, + np.sin(a)*LEFT + np.cos(a)*UP, + color = RED, + stroke_width = 2, + ) + for a in np.linspace(0, np.pi, 15) + ]) + lines.rotate(np.pi/2, RIGHT) + lines.rotate(np.pi/2, OUT) + + self.move_camera( + phi = np.pi/2, theta = 0, + added_anims = [ + Rotate(self.superposition_tex, np.pi/2), + ] + [ + ApplyMethod( + v.rotate_in_place, + -np.pi/2, + method_kwargs = {"axis" : v.get_vector()} + ) + for v in self.frozen_photon.mobject + ] + ) + self.play( + Write(words, run_time = 2), + self.superposition_tex.h_rect.set_stroke, RED, 3, + *map(GrowFromCenter, lines)+\ + [ + Animation(self.pol_filter), + Animation(self.frozen_photon.mobject) + ] + ) + self.dither(2) + self.move_camera( + phi = 0.8*np.pi/2, theta = -0.7*np.pi, + added_anims = [ + FadeOut(words), + Animation(lines), + Rotate(self.superposition_tex, -np.pi/2), + ] + [ + ApplyMethod( + v.rotate_in_place, + np.pi/2, + method_kwargs = {"axis" : v.get_vector()} + ) + for v in self.frozen_photon.mobject + ] + ) + self.play( + FadeOut(lines), + FadeOut(self.question), + self.superposition_tex.h_rect.fade, 1, + Animation(self.pol_filter) + ) + self.dither() + + self.absorption_words = words + + def probabalistic_passing_and_blocking(self): + absorption = self.get_filter_absorption_animation( + self.pol_filter, self.get_blocked_photon() + ) + prob = TexMobject("P(", "\\text{pass}", ")", "=", "1/2") + prob.highlight_by_tex("pass", GREEN) + prob.rotate(np.pi/2, RIGHT) + prob.next_to(self.superposition_tex, IN, MED_SMALL_BUFF, RIGHT) + + self.remove(self.frozen_photon.mobject) + self.play( + self.get_photon(), + rate_func = lambda t : min(t+self.pre_filter_alpha, 1), + ) + self.play( + FadeIn(prob), + self.get_blocked_photon(), + absorption + ) + bools = 4*[True] + 4*[False] + random.shuffle(bools) + for should_pass in bools: + if should_pass: + self.play(self.get_photon(), run_time = 1) + else: + self.play( + self.get_blocked_photon(), + Animation(self.axes), + absorption, + run_time = 1 + ) + self.play(FadeOut(prob)) + + def note_change_in_polarization(self): + words = TextMobject( + "``Collapses'' \\\\ from", "$|\\!\\nearrow\\rangle$", + "to", "$|\\!\\uparrow\\rangle$" + ) + words.highlight_by_tex("nearrow", E_COLOR) + words.highlight_by_tex("uparrow", GREEN) + words.next_to(ORIGIN, RIGHT, MED_LARGE_BUFF) + words.shift(2*UP) + words.rotate(np.pi/2, RIGHT) + photon = self.get_photon(run_time = 4) + for vect in photon.mobject: + if vect.get_center()[0] > 0: + vect.saved_state.set_fill(GREEN) + + self.play(FadeIn(words), photon) + for x in range(3): + self.play(photon) + + ###### + + def get_photon(self, **kwargs): + kwargs["run_time"] = kwargs.get("run_time", 1) + kwargs["include_M_vects"] = False + return WavePacket(em_wave = self.em_wave.copy(), **kwargs) + + def get_blocked_photon(self, **kwargs): + return self.get_photon(self, get_filtered = True, **kwargs) + +class PhotonPassesCompletelyOrNotAtAllStub(ExternallyAnimatedScene): + pass + +class ThreeFilters(ShootPhotonThroughFilter): + CONFIG = { + "filter_x_coordinates" : [-4, 0, 4], + "pol_filter_configs" : [ + {"filter_angle" : 0}, + {"filter_angle" : np.pi/4}, + {"filter_angle" : np.pi/2}, + ], + "EMWave_config" : { + "A_vect" : [0, 0, 1], + "amplitude" : 1.5, + "n_vectors" : 60, + }, + "line_start_length" : 8, + "line_end_length" : 8, + "n_lines" : 20, + "lines_depth" : 1.8, + "lines_shift_vect" : SMALL_BUFF*OUT, + "random_seed" : 6, + } + def construct(self): + self.remove(self.axes) + self.setup_filters() + self.setup_lines() + self.setup_arrows() + + self.fifty_percent_pass_second() + self.show_changed_to_diagonal() + self.fifty_percent_to_pass_third() + self.show_lines_with_middle() + self.remove_middle_then_put_back() + + def setup_filters(self): + for pf in self.pol_filters: + pf.arrow_label.rotate(np.pi/2, OUT) + pf.arrow_label.next_to(pf.arrow, RIGHT) + pf.arrow_label.rotate(np.pi/2, LEFT) + pf.arrow_label.add_background_rectangle() + pf.arrow_label.rotate(np.pi/2, RIGHT) + self.add_foreground_mobject(pf.arrow_label) + + def setup_lines(self): + lines_group = VGroup(*[ + self.get_lines(pf1, pf2, ratio) + for pf1, pf2, ratio in zip( + [None] + list(self.pol_filters), + list(self.pol_filters) + [None], + [1, 1, 0.5, 0.25] + ) + ]) + lines = lines_group[0] + spacing = lines[1].get_start() - lines[0].get_start() + lines.add(lines.copy().shift(spacing/2)) + self.lines_group = lines_group + + self.A_to_C_lines = self.get_lines( + self.pol_filters[0], self.pol_filters[2], + ) + + def setup_arrows(self): + for E_vect in self.em_wave.E_vects: + E_vect.normal_vector = IN+DOWN + self.em_wave.update(0) + + def fifty_percent_pass_second(self): + arrow = Arrow( + ORIGIN, 3*RIGHT, + use_rectangular_stem = False, + path_arc = -0.8*np.pi + ) + label = TexMobject("50\\%") + label.next_to(arrow, UP) + group = VGroup(arrow, label) + group.rotate(np.pi/2, RIGHT) + group.next_to(self.pol_filters[1], OUT, buff = 0) + group.highlight(BLUE) + + l1, l2, l3 = self.lines_group[:3] + pf1, pf2, pf3 = self.pol_filters + kwargs = { + "submobject_mode" : "all_at_once", + "rate_func" : None, + } + + self.play(ShowCreation(l1, run_time = 1, **kwargs)) + self.play( + ShowCreation(l2, **kwargs), + Animation(VGroup(pf1, l1)), + ShowCreation(arrow), + run_time = 0.5, + ) + self.play( + ShowCreation(l3, **kwargs), + Animation(VGroup(pf2, l2, pf1, l1)), + FadeIn(label), + run_time = 0.5, + ) + self.dither(2) + self.play( + FadeOut(l3), + Animation(pf2), + FadeOut(l2), + Animation(pf1), + FadeOut(l1) + ) + + self.fifty_percent_arrow_group = group + + def show_changed_to_diagonal(self): + photon = self.get_photon( + run_time = 2, + rate_func = lambda x : 0.6*x, + remover = False, + ) + brace = Brace(Line(1.5*LEFT, 1.5*RIGHT), DOWN) + label = brace.get_text( + "Changed to", + "$|\\!\\nearrow\\rangle$" + ) + label.highlight_by_tex("rangle", BLUE) + group = VGroup(brace, label) + group.rotate(np.pi/2, RIGHT) + group.shift(2*RIGHT + 0.5*IN) + + self.play(photon) + self.play( + GrowFromCenter(brace), + Write(label, run_time = 1) + ) + kwargs = { + "run_time" : 3, + "rate_func" : there_and_back_with_pause, + } + self.move_camera( + phi = np.pi/2, + theta = 0, + added_anims = [ + Rotate( + v, np.pi/2, + axis = v.get_vector(), + in_place = True, + **kwargs + ) + for v in photon.mobject + ] + [ + Rotate( + label, np.pi/2, + axis = OUT, + in_place = True, + **kwargs + ), + ], + **kwargs + ) + self.dither() + + self.photon = photon + self.brace_group = VGroup(brace, label) + + def fifty_percent_to_pass_third(self): + arrow_group = self.fifty_percent_arrow_group.copy() + arrow_group.shift(4*RIGHT) + arrow, label = arrow_group + + a = self.photon.rate_func(1) + new_photon = self.get_photon( + rate_func = lambda x : (1-a)*x + a, + run_time = 1 + ) + + self.revert_to_original_skipping_status() + self.play( + ShowCreation(arrow), + Write(label, run_time = 1) + ) + self.remove(self.photon.mobject) + self.play(new_photon) + + self.second_fifty_percent_arrow_group = arrow_group + + def show_lines_with_middle(self): + l1, l2, l3, l4 = self.lines_group + pf1, pf2, pf3 = self.pol_filters + + self.play( + FadeIn(l4), + Animation(pf3), + FadeIn(l3), + Animation(pf2), + FadeIn(l2), + Animation(pf1), + FadeIn(l1), + FadeOut(self.brace_group) + ) + self.dither(2) + + def remove_middle_then_put_back(self): + l1, l2, l3, l4 = self.lines_group + pf1, pf2, pf3 = self.pol_filters + mid_lines = self.A_to_C_lines + mover = VGroup( + pf2, + self.fifty_percent_arrow_group, + self.second_fifty_percent_arrow_group, + ) + + arrow = Arrow( + ORIGIN, 7*RIGHT, + use_rectangular_stem = False, + path_arc = 0.5*np.pi, + ) + labels = VGroup(*map(TexMobject, ["0\\%", "25\\%"])) + labels.scale(1.5) + labels.next_to(arrow, DOWN) + group = VGroup(arrow, labels) + group.rotate(np.pi/2, RIGHT) + group.shift(2*LEFT + IN) + group.highlight(GREEN) + + self.remove(l2, l3) + self.play( + FadeOut(l4), + Animation(pf3), + FadeOut(l3), + ApplyMethod( + mover.shift, 3*OUT, + rate_func = running_start + ), + ReplacementTransform(l2.copy(), mid_lines), + Animation(pf1), + Animation(l1) + ) + self.play( + ShowCreation(arrow), + Write(labels[0], run_time = 1) + ) + self.dither(2) + self.play( + FadeIn(l4), + Animation(pf3), + FadeOut(mid_lines), + FadeIn(l3), + mover.shift, 3*IN, + FadeIn(l2), + Animation(pf1), + Animation(l1) + ) + self.play(ReplacementTransform(*labels)) + self.dither(3) + + + #### + + def get_photon(self, **kwargs): + return ShootPhotonThroughFilter.get_photon(self, width = 4, **kwargs) + + def get_lines(self, filter1 = None, filter2 = None, ratio = 1.0): + n = self.n_lines + start, end = [ + (f.point_from_proportion(0.75) if f is not None else None) + for f in filter1, filter2 + ] + if start is None: + start = end + self.line_start_length*LEFT + if end is None: + end = start + self.line_end_length*RIGHT + nudge = (float(self.lines_depth)/self.n_lines)*OUT + lines = VGroup(*[ + Line(start, end).shift(z*nudge) + for z in range(n) + ]) + lines.set_stroke(YELLOW, 2) + lines.move_to(start, IN+LEFT) + lines.shift(self.lines_shift_vect) + n_to_block = int((1-ratio)*self.n_lines) + random.seed(self.random_seed) + indices_to_block = random.sample( + range(self.n_lines), n_to_block + ) + VGroup(*[lines[i] for i in indices_to_block]).set_stroke(width = 0) + return lines