from big_ol_pile_of_manim_imports import * from tqdm import tqdm as ProgressDisplay from .waves import * from functools import reduce #force_skipping #revert_to_original_skipping_status class PhotonPassesCompletelyOrNotAtAll(DirectionOfPolarizationScene): CONFIG = { "pol_filter_configs" : [{ "include_arrow_label" : False, "label_tex" : "\\text{Filter}", }], "EMWave_config" : { "wave_number" : 0, "A_vect" : [0, 1, 1], "start_point" : FRAME_X_RADIUS*LEFT + DOWN + 1.5*OUT, }, "start_theta" : -0.9*np.pi, "target_theta" : -0.6*np.pi, "apply_filter" : True, "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() self.remove(self.em_wave) def construct(self): pol_filter = self.pol_filter pol_filter.shift(0.5*OUT) lower_filter = pol_filter.copy() lower_filter.save_state() pol_filter.remove(pol_filter.label) passing_words = TextMobject("Photon", "passes through\\\\", "entirely") passing_words.set_color(GREEN) filtered_words = TextMobject("Photon", "is blocked\\\\", "entirely") filtered_words.set_color(RED) for words in passing_words, filtered_words: words.next_to(ORIGIN, UP+LEFT) words.shift(2*UP) words.add_background_rectangle() words.rotate(np.pi/2, RIGHT) filtered_words.shift(self.lower_portion_shift) passing_photon = WavePacket( run_time = 2, get_filtered = False, em_wave = self.em_wave.copy() ) lower_em_wave = self.em_wave.copy() lower_em_wave.mobject.shift(self.lower_portion_shift) lower_em_wave.start_point += self.lower_portion_shift filtered_photon = WavePacket( run_time = 2, get_filtered = True, em_wave = lower_em_wave.copy() ) green_flash = ApplyMethod( pol_filter.set_fill, GREEN, rate_func = squish_rate_func(there_and_back, 0.4, 0.6), run_time = passing_photon.run_time ) self.play( DrawBorderThenFill(pol_filter), Write(pol_filter.label, run_time = 2), ) self.move_camera(theta = self.target_theta) self.play( FadeIn(passing_words), passing_photon, green_flash, ) self.play( lower_filter.restore, lower_filter.shift, self.lower_portion_shift, FadeIn(filtered_words) ) red_flash = ApplyMethod( lower_filter.set_fill, RED, rate_func = squish_rate_func(there_and_back, 0.4, 0.6), run_time = filtered_photon.run_time ) for x in range(4): self.play( passing_photon, filtered_photon, green_flash, red_flash, ) self.wait() class PhotonPassesCompletelyOrNotAtAllForWavesVideo(PhotonPassesCompletelyOrNotAtAll): CONFIG = { "show_M_vects" : False, } class DirectionOfPolarization(DirectionOfPolarizationScene): def construct(self): self.remove(self.pol_filter) self.axes.z_axis.rotate(np.pi/2, OUT) words = TextMobject("Polarization direction") words.next_to(ORIGIN, UP+RIGHT, LARGE_BUFF) words.shift(2*UP) words.rotate(np.pi/2, RIGHT) words.rotate(-np.pi/2, OUT) em_wave = self.em_wave self.add(em_wave) self.wait(2) self.move_camera( phi = self.target_phi, theta = self.target_theta ) self.play(Write(words, run_time = 1)) for angle in 2*np.pi/3, -np.pi/3, np.pi/4: self.change_polarization_direction(angle) self.wait() self.wait(2) class PhotonsThroughPerpendicularFilters(PhotonPassesCompletelyOrNotAtAll): CONFIG = { "filter_x_coordinates" : [-2, 2], "pol_filter_configs" : [ {"filter_angle" : 0}, {"filter_angle" : np.pi/2}, ], "start_theta" : -0.9*np.pi, "target_theta" : -0.6*np.pi, "EMWave_config" : { "A_vect" : [0, 0, 1], "start_point" : FRAME_X_RADIUS*LEFT + DOWN + OUT, }, "apply_filter" : False, } def construct(self): photons = self.get_photons() prob_text = self.get_probability_text() self.pol_filters = VGroup(*reversed(self.pol_filters)) ninety_filter, zero_filter = self.pol_filters self.remove(*self.pol_filters) self.play(DrawBorderThenFill(zero_filter), run_time = 1) self.add_foreground_mobject(zero_filter) self.move_camera( theta = self.target_theta, added_anims = [ApplyFunction( self.reposition_filter_label, zero_filter )] ) self.reposition_filter_label(ninety_filter) self.play(self.get_photons()[2]) self.play(FadeIn(ninety_filter)) self.add_foreground_mobject(ninety_filter) self.shoot_photon() self.shoot_photon() self.play(FadeIn(prob_text)) for x in range(6): self.shoot_photon() def reposition_filter_label(self, pf): pf.arrow_label.rotate_in_place(np.pi/2, OUT) pf.arrow_label.next_to(pf.arrow, RIGHT) return pf def shoot_photon(self, *added_anims): photon = self.get_photons()[1] pol_filter = self.pol_filters[0] absorption = self.get_filter_absorption_animation(pol_filter, photon) self.play(photon, absorption) def get_photons(self): self.reference_line.rotate(np.pi/4) self.continual_update() return [ WavePacket( filter_distance = FRAME_X_RADIUS + x, get_filtered = True, em_wave = self.em_wave.copy(), run_time = 1, ) for x in (-2, 2, 10) ] def get_probability_text(self, prob = 0): prob_text = TexMobject( "P(", "\\substack", "{\\text{photons that make it} \\\\ ", " \\text{here } ", "\\text{make it}", " \\text{ here} }", ")", "=", str(int(prob*100)), "\\%", arg_separator = "" ) here1, here2 = prob_text.get_parts_by_tex("here") here1.set_color(GREEN) here2.set_color(RED) prob_text.add_background_rectangle() prob_text.next_to(ORIGIN, UP+RIGHT) prob_text.shift(2.5*UP+LEFT) prob_text.rotate(np.pi/2, RIGHT) arrows = [ Arrow( here.get_edge_center(IN), DOWN+OUT + x*RIGHT, color = here.get_color(), normal_vector = DOWN+OUT, ) for here, x in ((here1, 0), (here2, 4)) ] prob_text.add(*arrows) return prob_text class MoreFiltersMoreLight(FilterScene): CONFIG = { "filter_x_coordinates" : list(range(-2, 3)), "pol_filter_configs" : [ { "include_arrow_label" : False, "filter_angle" : angle } for angle in np.linspace(0, np.pi/2, 5) ], "ambient_rotation_rate" : 0, "arrow_rgb" : (0, 0, 0), "background_rgb" : (245, 245, 245), } def construct(self): self.remove(self.axes) pfs = VGroup(*reversed(self.pol_filters)) self.color_filters(pfs) self.remove(pfs) self.build_color_map(pfs) self.add(pfs[0], pfs[2], pfs[4]) pfs.center().scale(1.5) self.move_camera( phi = 0.9*np.pi/2, theta = -0.95*np.pi, ) self.play( Animation(pfs[0]), pfs[2].shift, 3*OUT, Animation(pfs[4]), ) self.wait() self.play( Animation(pfs[0]), pfs[2].shift, 3*IN, Animation(pfs[4]), ) pfs[1].shift(8*OUT) self.play( Animation(pfs[0]), pfs[1].shift, 8*IN, Animation(VGroup(pfs[2], pfs[4])), run_time = 2 ) self.wait() pfs[3].shift(8*OUT) self.play( Animation(VGroup(*pfs[:3])), pfs[3].shift, 8*IN, Animation(VGroup(*pfs[4:])), run_time = 2 ) self.wait() def color_filters(self, pfs): colors = [RED, GREEN, BLUE, MAROON_B, PURPLE_C] for pf, color in zip(pfs, colors): pf.set_fill(color, 0.5) pf.arrow.set_fill(WHITE, 1) turn_off_3d_shading(pfs) def build_color_map(self, pfs): phi, theta = self.camera.get_phi(), self.camera.get_theta() self.set_camera_orientation(np.pi/2, -np.pi) self.original_rgbas = [(255, 255, 255)] self.new_rgbas = [self.arrow_rgb] for bool_array in it.product(*5*[[True, False]]): pfs_to_use = VGroup(*[ pf for pf, b in zip(pfs, bool_array) if b ]) self.camera.capture_mobject(pfs_to_use) frame = self.camera.get_image() h, w, three = frame.shape rgb = frame[3*h/8, 7*w/12] self.original_rgbas.append(rgb) angles = [pf.filter_angle for pf in pfs_to_use] p = 0.5 for a1, a2 in zip(angles, angles[1:]): p *= np.cos(a2 - a1)**2 new_rgb = (255*p*np.ones(3)).astype(int) if not any(bool_array): new_rgb = self.background_rgb self.new_rgbas.append(new_rgb) self.camera.reset() self.set_camera_orientation(phi, theta) def update_frame(self, mobjects = None, image = None): FilterScene.update_frame(self, mobjects) def get_frame(self): frame = FilterScene.get_frame(self) bool_arrays = [ (frame[:,:,0] == r) & (frame[:,:,1] == g) & (frame[:,:,2] == b) for (r, g, b) in self.original_rgbas ] for ba, new_rgb in zip(bool_arrays, self.new_rgbas): frame[ba] = new_rgb covered = reduce( lambda b1, b2 : b1 | b2, bool_arrays ) frame[~covered] = [65, 65, 65] return frame class MoreFiltersMoreLightBlackBackground(MoreFiltersMoreLight): CONFIG = { "arrow_rgb" : (255, 255, 255), "background_rgb" : (0, 0, 0), } class ConfusedPiCreature(Scene): def construct(self): randy = Randolph() self.play( randy.change, "confused", 3*(UP+RIGHT), ) self.play(Blink(randy)) self.wait(2) self.play(Blink(randy)) self.wait(2) class AngryPiCreature(PiCreatureScene): def construct(self): self.pi_creature_says( "No, \\emph{locality} \\\\ must be wrong!", target_mode = "angry", look_at_arg = 2*RIGHT, run_time = 1 ) self.wait(3) def create_pi_creature(self): return Randolph().shift(DOWN+3*LEFT) class ShowALittleMath(TeacherStudentsScene): def construct(self): exp1 = TexMobject( "|", "\\psi", "\\rangle = ", "\\alpha", "|\\uparrow\\rangle", "+", "\\beta", "|\\rightarrow\\rangle" ) exp2 = TexMobject( "|| \\langle", "\\psi", "|", "\\psi", "\\rangle ||^2", "= ", "\\alpha", "^2", "+", "\\beta", "^2" ) color_map = { "alpha" : GREEN, "beta" : RED, "psi" : BLUE } for exp in exp1, exp2: exp.set_color_by_tex_to_color_map(color_map) exp1.next_to(self.teacher.get_corner(UP+LEFT), UP, LARGE_BUFF) exp2.move_to(exp1) self.play( Write(exp1, run_time = 2), self.teacher.change, "raise_right_hand" ) self.play(exp1.shift, UP) self.play(*[ ReplacementTransform( exp1.get_parts_by_tex(tex).copy(), exp2.get_parts_by_tex(tex).copy(), ) for tex in list(color_map.keys()) ] + [Write(exp2, run_time = 2)]) self.change_student_modes( *["pondering"]*3, look_at_arg = exp2 ) self.wait(2) class SecondVideoWrapper(Scene): def construct(self): title = TextMobject("Some light quantum mechanics") title.to_edge(UP) self.add(title) screen_rect = ScreenRectangle(height = 6) screen_rect.next_to(title, DOWN) self.play(ShowCreation(screen_rect)) self.wait(3) class BasicsOfPolarization(DirectionOfPolarizationScene): CONFIG = { "apply_filter" : True, } def construct(self): self.setup_rectangles() self.show_continual_wave() self.show_photons() def show_continual_wave(self): em_wave = self.em_wave title = TextMobject("Waves in the ``electromagnetic field''") title.to_edge(UP) subtitle = TextMobject("Polarization = Direction of", "wiggling") subtitle.set_color_by_tex("wiggling", BLUE) subtitle.next_to(title, DOWN) for words in title, subtitle: words.add_background_rectangle() words.rotate(np.pi/2, RIGHT) self.play(Write(title)) self.wait(2) self.play( Write(subtitle, run_time = 2), FadeIn(self.rectangles) ) self.change_polarization_direction(np.pi/2, run_time = 3) self.wait() self.change_polarization_direction(-np.pi/12, run_time = 2) self.move_camera(theta = -0.95*np.pi) self.change_polarization_direction(-np.pi/6, run_time = 2) self.change_polarization_direction(np.pi/6, run_time = 2) self.move_camera(theta = -0.6*np.pi) self.change_polarization_direction(-np.pi/6, run_time = 2) self.change_polarization_direction(np.pi/6, run_time = 2) self.change_polarization_direction(-5*np.pi/12, run_time = 2) self.play( FadeOut(em_wave.mobject), FadeOut(self.rectangles), ) self.remove(em_wave) self.reference_line.put_start_and_end_on(ORIGIN, RIGHT) def show_photons(self): quantum_left_words = TextMobject( "Quantum", "$\\Rightarrow$", ) quantum_left_words.next_to(ORIGIN, UP+RIGHT) quantum_left_words.shift(UP) quantum_right_words = TextMobject( "Completely through", "or \\\\", "Completely blocked", ) quantum_right_words.scale(0.8) quantum_right_words.next_to(quantum_left_words, buff = 0) quantum_right_words.set_color_by_tex("through", GREEN) quantum_right_words.set_color_by_tex("blocked", RED) quantum_words = VGroup(quantum_left_words, quantum_right_words) quantum_words.rotate(np.pi/2, RIGHT) prob_eq = TexMobject( "&P(", "\\text{Pass}", ")", "=", "p\\\\", "&P(", "\\text{Blocked}", ")", "=", "1-p", ) prob_eq.set_color_by_tex_to_color_map({ "Pass" : GREEN, "Blocked" : RED, }) prob_eq.next_to(ORIGIN, DOWN+RIGHT) prob_eq.shift(RIGHT) prob_eq.rotate(np.pi/2, RIGHT) config = dict(self.EMWave_config) config.update({ "wave_number" : 0, "A_vect" : [0, 1, -1], }) self.em_wave = EMWave(**config) self.continual_update() passing_photon = WavePacket( em_wave = self.em_wave.copy(), run_time = 1.5, ) filtered_photon = WavePacket( em_wave = self.em_wave.copy(), get_filtered = True, run_time = 1.5, ) self.play(FadeIn( quantum_words, run_time = 2, submobject_mode = "lagged_start" )) anim_sets = [ [passing_photon], [ filtered_photon, self.get_filter_absorption_animation( self.pol_filter, filtered_photon ) ], ] for index in 0, 1: self.play(*anim_sets[index]) self.play( # FadeIn(prob_eq, submobject_mode = "lagged_start"), passing_photon ) for index in 1, 0, 0, 1: self.play(*anim_sets[index]) class AngleToProbabilityChart(Scene): def construct(self): left_title = TextMobject("Angle between \\\\ filters") left_title.to_corner(UP+LEFT) right_title = TextMobject( "Probability that photons passing \\\\", "through the first pass through the second" ) right_title.next_to(left_title, RIGHT, LARGE_BUFF) h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS) h_line.to_edge(UP, buff = 2) v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS) v_line.next_to(left_title, RIGHT, MED_LARGE_BUFF) v_line.to_edge(UP, buff = 0) VGroup(h_line, v_line).set_color(BLUE) self.add(left_title, right_title, h_line, v_line) angles = [0, 22.5, 45, 67.5, 90] angle_mobs = VGroup(*[ TexMobject(str(angle) + "^\\circ") for angle in angles ]) angle_mobs.arrange_submobjects(DOWN, buff = MED_LARGE_BUFF) angle_mobs.next_to(left_title, DOWN, LARGE_BUFF) probs = [ np.cos(angle*np.pi/180.0)**2 for angle in angles ] prob_mobs = VGroup(*[ TexMobject("%.1f"%(100*prob) + "\\%") for prob in probs ]) prob_mobs.set_color(YELLOW) angle_prob_pairs = list(zip(angle_mobs, prob_mobs)) for angle_mob, prob_mob in angle_prob_pairs: prob_mob.next_to(angle_mob, RIGHT, buff = 3) for prob_mob in prob_mobs[1:]: prob_mob.align_to(prob_mobs[0], LEFT) for i in [0, 4, 2, 1, 3]: self.play(FadeIn(angle_mobs[i])) self.play(ReplacementTransform( angle_mobs[i].copy(), prob_mobs[i] )) explanation = TextMobject("Based on $\\cos(\\theta)^2$") explanation.next_to(prob_mobs, RIGHT, LARGE_BUFF) self.play(Write(explanation, run_time = 2)) self.wait() class ShowVariousFilterPairsWithPhotonsOverTime(PhotonsThroughPerpendicularFilters): CONFIG = { "filter_x_coordinates" : [-2, 2, 2, 2, 2], "pol_filter_configs" : [ {"filter_angle" : angle} for angle in (0, 0, np.pi/2, np.pi/4, np.pi/8) ], "apply_filter" : False, } def setup(self): PhotonsThroughPerpendicularFilters.setup(self) self.new_filters = self.pol_filters[2:] self.remove(*self.new_filters) self.pol_filters = self.pol_filters[:2] def construct(self): self.photons = self.get_photons() self.add_filters() self.add_probability_text() self.show_photons() for pol_filter in self.new_filters: self.change_to_new_filter(pol_filter) self.show_photons() def add_filters(self): self.remove(*self.pol_filters[1:]) self.wait() self.play(ReplacementTransform( self.pol_filters[0].copy().set_fill(BLACK, 1), self.pol_filters[1] )) self.move_camera( theta = -0.65*np.pi, added_anims = list(it.chain(*[ [ pf.arrow_label.rotate_in_place, np.pi/2, OUT, pf.arrow_label.next_to, pf.arrow, RIGHT ] for pf in self.pol_filters[:2] ])) ) for pf in self.new_filters: pf.arrow_label.rotate_in_place(np.pi/2, OUT) pf.arrow_label.next_to(pf.arrow, RIGHT) self.second_filter = self.pol_filters[1] self.add_foreground_mobject(self.second_filter) def add_probability_text(self): prob_text = self.get_probability_text(self.get_prob()) self.play(FadeIn(prob_text)) self.prob_text = prob_text def show_photons(self, n_photons = 5): p = self.get_prob() blocked_photon = copy.deepcopy(self.photons[0]) blocked_photon.rate_func = squish_rate_func( lambda x : x, 0, 0.5, ) first_absorption = self.get_filter_absorption_animation( self.pol_filters[0], blocked_photon ) first_absorption.rate_func = squish_rate_func( first_absorption.rate_func, 0, 0.5, ) photons = [ copy.deepcopy(self.photons[2 if q <= p else 1]) for q in np.arange(0, 1, 1./n_photons) ] random.shuffle(photons) for photon in photons: photon.rate_func = squish_rate_func( lambda x : x, 0.5, 1 ) added_anims = [] if photon.filter_distance == FRAME_X_RADIUS + 2: absorption = self.get_filter_absorption_animation( self.second_filter, photon ) absorption.rate_func = squish_rate_func( absorption.rate_func, 0.5, 1 ) added_anims.append(absorption) self.play( blocked_photon, first_absorption, photon, *added_anims ) self.wait() def change_to_new_filter(self, pol_filter, added_anims = None): if added_anims is None: added_anims = [] self.play( Transform(self.second_filter, pol_filter), *added_anims ) self.second_filter.filter_angle = pol_filter.filter_angle new_prob_text = self.get_probability_text(self.get_prob()) new_prob_text[1][-2].set_color(YELLOW) self.play(Transform(self.prob_text, new_prob_text)) #### def get_prob(self, pol_filter = None): if pol_filter is None: pol_filter = self.second_filter return np.cos(pol_filter.filter_angle)**2 class ShowVariousFilterPairs(ShowVariousFilterPairsWithPhotonsOverTime): CONFIG = { "filter_x_coordinates" : [], "filter_z_coordinates" : [2.5, 0, -2.5], "angles" : [0, np.pi/4, np.pi/2], "n_lines" : 20, "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, } def setup(self): ShowVariousFilterPairsWithPhotonsOverTime.setup(self) self.remove(*self.pol_filters) self.prev_groups = VGroup() self.remove(self.axes) self.setup_filters() self.stop_ambient_camera_rotation() self.prob_texts = VGroup() def setup_filters(self): self.filter_pairs = [] zs = self.filter_z_coordinates for non_zero_angle, z in zip(self.angles, zs): filter_pair = VGroup() for angle, x in (0, -3), (non_zero_angle, 3): pf = PolarizingFilter(filter_angle = angle) pf.scale(0.7) pf.rotate(np.pi/2, RIGHT) pf.rotate(np.pi/2, IN) pf.shift(x*RIGHT + z*OUT) pf.arrow_label.rotate(np.pi/2, OUT) pf.arrow_label.next_to(pf.arrow, RIGHT, SMALL_BUFF) filter_pair.add(pf) self.filter_pairs.append(filter_pair) def construct(self): self.add_top_filters() self.show_light(self.filter_pairs[0]) self.turn_one_filter_pair_into_another(0, 2) self.show_light(self.filter_pairs[2]) self.turn_one_filter_pair_into_another(2, 1) self.show_light(self.filter_pairs[1]) def add_top_filters(self): pf1, pf2 = pair = self.filter_pairs[0] for pf in pair: pf.save_state() pf.arrow_label.rotate(np.pi/2, IN) pf.arrow_label.next_to(pf.arrow, UP, SMALL_BUFF) pf.shift(2*IN) self.add(pf1) self.play( ReplacementTransform(pf1.copy().fade(1), pf2), Animation(pf1) ) self.move_camera( 0.9*np.pi/2, -0.6*np.pi, added_anims = [ pf.restore for pf in pair ] ) def show_light(self, filter_pair): pf1, pf2 = filter_pair lines_to_pf1 = self.get_lines(None, pf1) vect = lines_to_pf1[1].get_center() - lines_to_pf1[0].get_center() lines_to_pf1.add(*lines_to_pf1.copy().shift(vect/2)) lines_to_pf2 = self.get_lines(pf1, pf2) lines_from_pf2 = self.get_lines(pf2) prob = self.get_prob(pf2) n_black = int(prob*len(lines_from_pf2)) VGroup(*lines_from_pf2[n_black:]).set_stroke(BLACK, 0) kwargs = { "rate_func" : None, "submobject_mode" : "all_at_once", } self.play(ShowCreation(lines_to_pf1, run_time = 2./3, **kwargs)) self.play( ShowCreation(lines_to_pf2, **kwargs), Animation(VGroup(pf1, lines_to_pf1)), run_time = 1./6, ) self.play( ShowCreation(lines_from_pf2, **kwargs), Animation(VGroup(pf2, lines_to_pf2, pf1, lines_to_pf1)), run_time = 2./3, ) group = VGroup( lines_from_pf2, pf2, lines_to_pf2, pf1, lines_to_pf2 ) #Write probability prob_text = self.get_probability_text(pf2) self.play(Write(prob_text, run_time = 1)) self.wait() self.prob_texts.add(prob_text) def turn_one_filter_pair_into_another(self, i1, i2): mover = self.filter_pairs[i1].copy() mover.set_stroke(width = 0) mover.set_fill(opacity = 0) target = self.filter_pairs[i2] self.play(ReplacementTransform(mover, target)) def get_probability_text(self, pol_filter = None): if pol_filter is None: pol_filter = self.second_filter prob = self.get_prob(pol_filter) prob_mob = TextMobject(str(int(prob*100)) + "\\%", " pass") prob_mob.scale(0.7) prob_mob.rotate(np.pi/2, RIGHT) prob_mob.next_to(pol_filter.arrow_label, RIGHT) prob_mob.set_color( list(Color(RED).range_to(GREEN, 11))[int(prob*10)] ) return prob_mob ##### def get_lines( self, filter1 = None, filter2 = None, ratio = 1.0, remove_from_bottom = False, ): # n = int(ratio*self.n_lines) 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) if remove_from_bottom: to_block = lines[:n_to_block] else: to_block = lines[len(lines)-n_to_block:] VGroup(*to_block).set_stroke(width = 0) return lines class ShowVariousFilterPairsFrom0To45(ShowVariousFilterPairs): CONFIG = { "angles" : [0, np.pi/8, np.pi/4] } def construct(self): ShowVariousFilterPairs.construct(self) self.mention_probabilities() def mention_probabilities(self): rects = VGroup() for prob_text in self.prob_texts: prob_text.rotate(np.pi/2, LEFT) rect = SurroundingRectangle(prob_text, color = BLUE) VGroup(prob_text, rect).rotate(np.pi/2, RIGHT) rects.add(rect) cosines = VGroup(*[ TexMobject("\\cos^2(%s^\\circ)"%str(x)) for x in (45, 22.5) ]) cosines.scale(0.8) # cosines.set_color(BLUE) cosines.rotate(np.pi/2, RIGHT) for cos, rect in zip(cosines, rects[1:]): cos.next_to(rect, OUT, SMALL_BUFF) self.play(LaggedStart(ShowCreation, rects)) self.wait() self.play(*list(map(Write, cosines)), run_time = 2) self.wait() class ForgetPreviousActions(ShowVariousFilterPairs): CONFIG = { "filter_x_coordinates" : [-6, -2, 2, 2, 2], "pol_filter_configs" : [ {"filter_angle" : angle} for angle in (np.pi/4, 0, np.pi/4, np.pi/3, np.pi/6) ], "start_theta" : -0.6*np.pi, "EMWave_config" : { "wave_number" : 0, "start_point" : FRAME_X_RADIUS*LEFT + DOWN, }, "apply_filter" : False, } def setup(self): PhotonsThroughPerpendicularFilters.setup(self) self.remove(self.axes) VGroup(*self.pol_filters).shift(IN) for pf in self.pol_filters: pf.arrow_label.rotate_in_place(np.pi/2, OUT) pf.arrow_label.next_to(pf.arrow, RIGHT) self.stop_ambient_camera_rotation() def construct(self): front_filter = self.pol_filters[0] first_filter = self.pol_filters[1] possible_second_filters = self.pol_filters[2:] for pf in possible_second_filters: prob_text = self.get_probability_text(pf) prob_text.scale(1.3, about_point = prob_text.get_left()) pf.add(prob_text) second_filter = possible_second_filters[0].copy() self.second_filter = second_filter self.pol_filters = VGroup( first_filter, second_filter ) self.remove(front_filter) self.remove(*possible_second_filters) self.add(second_filter) self.apply_filter = True self.continual_update() self.photons = self.get_photons()[1:] group = VGroup(*self.pol_filters) rect1 = SurroundingRectangle(group) rect1.rotate_in_place(np.pi/2, RIGHT) rect1.rescale_to_fit(group.get_depth()+MED_SMALL_BUFF, 2, True) rect1.stretch_in_place(1.2, 0) prob_words = TextMobject( "Probabilities depend only\\\\", "on this angle difference" ) prob_words.add_background_rectangle() prob_words.rotate(np.pi/2, RIGHT) prob_words.next_to(rect1, OUT) self.add(rect1) self.play(FadeIn(prob_words)) for index in 1, 2: self.shoot_photon() self.play(Transform( second_filter, possible_second_filters[index] )) rect2 = SurroundingRectangle(front_filter, color = RED) rect2.rotate_in_place(np.pi/2, RIGHT) rect2.rescale_to_fit(front_filter.get_depth()+MED_SMALL_BUFF, 2, True) rect2.stretch_in_place(1.5, 0) ignore_words = TextMobject("Photon \\\\", "``forgets'' this") ignore_words.add_background_rectangle() ignore_words.rotate(np.pi/2, RIGHT) ignore_words.next_to(rect2, OUT) self.play( ShowCreation(rect2), Write(ignore_words, run_time = 1), FadeIn(front_filter), run_time = 1.5, ) self.shoot_photon() for index in 0, 1, 2: self.play(Transform( second_filter, possible_second_filters[index] )) self.shoot_photon() def shoot_photon(self): photon = random.choice(self.photons) added_anims = [] if photon.filter_distance == FRAME_X_RADIUS + 2: added_anims.append( ApplyMethod( self.second_filter.set_color, RED, rate_func = squish_rate_func(there_and_back, 0.5, 0.7) ) ) self.play(photon, *added_anims, run_time = 1.5) class IntroduceLabeledFilters(ShowVariousFilterPairs): CONFIG = { "filter_x_coordinates" : [-5, -2, 1], "pol_filter_configs" : [ {"filter_angle" : angle} for angle in [0, np.pi/8, np.pi/4] ], "start_phi" : 0.9*np.pi/2, "start_theta" : -0.85*np.pi, "target_theta" : -0.65*np.pi, "lines_depth" : 1.7, "lines_shift_vect" : MED_SMALL_BUFF*OUT, "line_start_length" : 3, "line_end_length" : 9, "ambient_rotation_rate" : 0.005, } def setup(self): PhotonsThroughPerpendicularFilters.setup(self) self.remove(self.axes) def construct(self): self.add_letters_to_labels() self.introduce_filters() self.reposition_camera() self.separate_cases() self.show_bottom_lines() self.comment_on_half_blocked_by_C() self.show_top_lines() self.comment_on_those_blocked_by_B() self.show_sum() def add_letters_to_labels(self): for char, pf, color in zip("ABC", self.pol_filters, [RED, GREEN, BLUE]): label = TextMobject(char) label.scale(0.9) label.add_background_rectangle() label.set_color(color) label.rotate(np.pi/2, RIGHT) label.rotate(np.pi/2, IN) label.next_to(pf.arrow_label, UP) pf.arrow_label.add(label) pf.arrow_label.next_to(pf.arrow, OUT, SMALL_BUFF) self.remove(*self.pol_filters) def introduce_filters(self): self.A_filter, self.B_filter, self.C_filter = self.pol_filters for pf in self.pol_filters: pf.save_state() pf.shift(4*OUT) pf.fade(1) self.play(pf.restore) self.wait() def reposition_camera(self): self.move_camera( theta = self.target_theta, added_anims = list(it.chain(*[ [ pf.arrow_label.rotate, np.pi/2, OUT, pf.arrow_label.next_to, pf.arrow, RIGHT ] for pf in self.pol_filters ])) ) def separate_cases(self): self.lower_pol_filters = VGroup( self.A_filter.deepcopy(), self.C_filter.deepcopy(), ) self.lower_pol_filters.save_state() self.lower_pol_filters.fade(1) self.play( self.lower_pol_filters.restore, self.lower_pol_filters.shift, 3*IN, self.pol_filters.shift, 1.5*OUT, ) self.wait() def show_bottom_lines(self): A, C = self.lower_pol_filters lines_to_A = self.get_lines(None, A) vect = lines_to_A[1].get_center() - lines_to_A[0].get_center() lines_to_A.add(*lines_to_A.copy().shift(vect/2)) lines_to_C = self.get_lines(A, C) lines_from_C = self.get_lines(C, ratio = 0.5) kwargs = { "rate_func" : None, "submobject_mode" : "all_at_once", "run_time" : 1./3, } self.play( ShowCreation(lines_to_A), **kwargs ) self.play( ShowCreation(lines_to_C), Animation(VGroup(A, lines_to_A)), **kwargs ) kwargs["run_time"] = 3*kwargs["run_time"] self.play( ShowCreation(lines_from_C), Animation(VGroup(C, lines_to_C, A, lines_to_A)), **kwargs ) line_group = VGroup(lines_from_C, C, lines_to_C, A, lines_to_A) self.remove(*line_group) self.add_foreground_mobject(line_group) self.bottom_lines_group = line_group def comment_on_half_blocked_by_C(self): arrow = Arrow( ORIGIN, 3.5*RIGHT, path_arc = -0.9*np.pi, use_rectangular_stem = False, color = BLUE, stroke_width = 5, ) words = TextMobject("50\\% blocked") words.set_color(BLUE) words.next_to(arrow, RIGHT, buff = 0) group = VGroup(arrow, words) group.rotate(np.pi/2, RIGHT) group.shift(1.7*IN + 0.5*RIGHT) self.play( Write(words, run_time = 2), ShowCreation(arrow) ) self.wait(2) self.blocked_at_C_words = words self.blocked_at_C_label_group = VGroup(arrow, words) def show_top_lines(self): A, B, C = self.pol_filters lines_to_A = self.get_lines(None, A) vect = lines_to_A[1].get_center() - lines_to_A[0].get_center() lines_to_A.add(*lines_to_A.copy().shift(vect/2)) lines_to_B = self.get_lines(A, B) lines_to_C = self.get_lines(B, C, ratio = 0.85, remove_from_bottom = True) lines_from_C = self.get_lines(C, ratio = 0.85**2, remove_from_bottom = True) kwargs = { "rate_func" : None, "submobject_mode" : "all_at_once", "run_time" : 1./5, } self.play( ShowCreation(lines_to_A), **kwargs ) self.play( ShowCreation(lines_to_B), Animation(VGroup(A, lines_to_A)), **kwargs ) self.play( ShowCreation(lines_to_C), Animation(VGroup(B, lines_to_B, A, lines_to_A)), **kwargs ) kwargs["run_time"] = 3*kwargs["run_time"] self.play( ShowCreation(lines_from_C), Animation(VGroup(C, lines_to_C, B, lines_to_B, A, lines_to_A)), **kwargs ) line_group = VGroup( lines_from_C, C, lines_to_C, B, lines_to_B, A, lines_to_A ) self.remove(*line_group) self.add_foreground_mobject(line_group) self.top_lines_group = line_group def comment_on_those_blocked_by_B(self): arrow0 = Arrow( 2*LEFT, 0.5*(UP+RIGHT), path_arc = 0.8*np.pi, use_rectangular_stem = False, color = WHITE, stroke_width = 5, buff = 0 ) arrow1 = Arrow( 2*LEFT, ORIGIN, path_arc = 0.8*np.pi, use_rectangular_stem = False, color = GREEN, stroke_width = 5, buff = 0 ) arrow2 = arrow1.copy() arrow2.next_to(arrow1, RIGHT, buff = LARGE_BUFF) words1 = TextMobject("15\\%", "blocked") words1.set_color(GREEN) words2 = words1.copy() words1.next_to(arrow1, DOWN, buff = SMALL_BUFF) words2.next_to(arrow2, DOWN, buff = SMALL_BUFF) words2.shift(MED_LARGE_BUFF*RIGHT) words0 = TextMobject("85\\%", "pass") words0.move_to(words1) group = VGroup(arrow0, arrow1, arrow2, words0, words1, words2) group.rotate(np.pi/2, RIGHT) group.shift(0.8*LEFT+1.5*OUT) self.play( ShowCreation(arrow0), Write(words0, run_time = 1) ) self.wait() self.play( ReplacementTransform(words0, words1), ReplacementTransform(arrow0, arrow1), ) self.wait() self.play( ShowCreation(arrow2), Write(words2) ) self.wait(2) self.fifteens = VGroup(words1, words2) self.blocked_at_B_label_group = VGroup( words1, words2, arrow1, arrow2 ) def show_sum(self): fifteen1, fifteen2 = self.fifteens fifty = self.blocked_at_C_words plus = TexMobject("+").rotate(np.pi/2, RIGHT) plus.move_to(Line(fifteen1.get_right(), fifteen2.get_left())) equals = TexMobject("=").rotate(np.pi/2, RIGHT) equals.next_to(fifteen2, RIGHT, 2*SMALL_BUFF) q_mark = TexMobject("?").rotate(np.pi/2, RIGHT) q_mark.next_to(equals, OUT, SMALL_BUFF) q_mark.set_color(RED) randy = Randolph(mode = "confused").flip() randy.scale(0.7) randy.rotate(np.pi/2, RIGHT) randy.move_to(fifty) randy.shift(0.5*RIGHT) randy.look_at(equals) blinked = randy.copy() blinked.rotate(np.pi/2, LEFT) blinked.blink() blinked.rotate(np.pi/2, RIGHT) self.play( fifteen1.set_color, YELLOW, Write(plus) ) self.play( fifteen2.set_color, YELLOW, Write(equals) ) self.play( fifty.next_to, equals, RIGHT, 2*SMALL_BUFF, Write(q_mark), FadeIn(randy) ) self.play(Transform( randy, blinked, rate_func = squish_rate_func(there_and_back) )) self.wait(3) class IntroduceLabeledFiltersNoRotation(IntroduceLabeledFilters): CONFIG = { "ambient_rotation_rate" : 0, "target_theta" : -0.55*np.pi, } class RemoveBFromLabeledFilters(IntroduceLabeledFiltersNoRotation): def construct(self): self.force_skipping() IntroduceLabeledFiltersNoRotation.construct(self) self.revert_to_original_skipping_status() self.setup_start() self.show_filter_B_removal() self.fade_in_labels() def show_sum(self): pass def setup_start(self): self.remove(self.blocked_at_C_label_group) self.remove(self.blocked_at_B_label_group) self.remove(self.bottom_lines_group) self.top_lines_group.save_state() self.top_lines_group.shift(2*IN) def show_filter_B_removal(self): top_lines_group = self.top_lines_group bottom_lines_group = self.bottom_lines_group mover = top_lines_group.copy() mover.save_state() mover.fade(1) sl1, sC, sl2, sB, sl3, sA, sl4 = mover tl1, tC, tl2, tA, tl3 = bottom_lines_group for line in tl2: line.scale(0.5, about_point = line.get_end()) kwargs = { "submobject_mode" : "all_at_once", "rate_func" : None, } self.play( top_lines_group.shift, 2*OUT, mover.restore, mover.shift, 2.5*IN, ) self.wait() self.play( ApplyMethod(sB.shift, 4*IN, rate_func = running_start), FadeOut(sl1), Animation(sC), FadeOut(sl2), ) self.play(ShowCreation(tl2, run_time = 0.25, **kwargs)) self.play( ShowCreation(tl1, run_time = 0.5, **kwargs), Animation(sC), Animation(tl2), ) self.wait() def fade_in_labels(self): self.play(*list(map(FadeIn, [ self.blocked_at_B_label_group, self.blocked_at_C_label_group, ]))) self.wait() class NumbersSuggestHiddenVariablesAreImpossible(TeacherStudentsScene): def construct(self): self.teacher_says( "These numbers suggest\\\\", "no hidden variables" ) self.change_student_modes("erm", "sassy", "confused") self.wait(3) class VennDiagramProofByContradiction(Scene): CONFIG = { "circle_colors" : [RED, GREEN, BLUE] } def construct(self): self.setup_venn_diagram_sections() self.draw_venn_diagram() self.show_100_photons() self.show_one_photon_answering_questions() self.put_all_photons_in_A() self.separate_by_B() self.separate_by_C() self.show_two_relevant_subsets() def draw_venn_diagram(self, send_to_corner = True): A, B, C = venn_diagram = VGroup(*[ Circle( radius = 3, stroke_width = 3, stroke_color = c, fill_opacity = 0.2, fill_color = c, ).shift(vect) for c, vect in zip( self.circle_colors, compass_directions(3, UP) ) ]) self.A_to_B_vect = B.get_center() - A.get_center() self.A_to_C_vect = C.get_center() - A.get_center() venn_diagram.center() labels = VGroup() alt_labels = VGroup() props = [1./12, 0.5, 0] angles = [0, np.pi/8, np.pi/4] for circle, char, prop, angle in zip(venn_diagram, "ABC", props, angles): label, alt_label = [ TextMobject( "%s \\\\"%start, "through", char + "$\\! \\uparrow$" ).set_color_by_tex(char, circle.get_color()) for start in ("Would pass", "Pass") ] for mob in label, alt_label: mob[-1][-1].rotate_in_place(-angle) mob[-1][-1].shift(0.5*SMALL_BUFF*UP) center = circle.get_center() label.move_to(center) label.generate_target() point = circle.point_from_proportion(prop) alt_label.scale(2) for mob in label.target, alt_label: mob.next_to(point, point-center, SMALL_BUFF) circle.label = label circle.alt_label = alt_label labels.add(label) alt_labels.add(alt_label) last_circle = None for circle in venn_diagram: added_anims = [] if last_circle: added_anims.append(MoveToTarget(last_circle.label)) self.play( DrawBorderThenFill(circle, run_time = 2), Write(circle.label, run_time = 2), *added_anims ) last_circle = circle self.play(MoveToTarget(last_circle.label)) self.wait() if hasattr(self, "A_segments"): A.add(self.A_segments) if send_to_corner: group = VGroup(venn_diagram, labels) target = VGroup(venn_diagram.copy(), alt_labels) target.scale(0.25) target.to_corner(UP+RIGHT) self.play(Transform(group, target)) self.remove(group) for circle in venn_diagram: circle.label = circle.alt_label self.add(circle) for circle in venn_diagram: self.add(circle.label) self.venn_diagram = venn_diagram def show_100_photons(self): photon = FunctionGraph( lambda x : -np.cos(3*np.pi*x)*np.exp(-x*x), x_min = -2, x_max = 2, color = YELLOW, stroke_width = 2, ) photon.shift(LEFT + 2*UP) eyes = Eyes(photon) photon.eyes = eyes hundred, photon_word, s = words = TextMobject( "100 ", "Photon", "s", arg_separator = "" ) words.next_to(eyes, UP) self.play( ShowCreation(photon), FadeIn(photon.eyes), Write(photon_word, run_time = 1.5) ) photon.add(photon.eyes) #Split to hundred photons = VGroup(*[photon.deepcopy() for x in range(100)]) self.arrange_photons_in_circle(photons) photons.set_height(6) photons.next_to(words, DOWN) photons.to_edge(LEFT) self.play( Write(hundred), Write(s), ReplacementTransform( VGroup(photon), photons, submobject_mode = "lagged_start" ) ) self.photons = photons self.photon_words = words def show_one_photon_answering_questions(self): photon = self.photons[89] photon.save_state() photon.generate_target() answers = TextMobject( "Pass through A?", "Yes\\\\", "Pass through B?", "No\\\\", "Pass through C?", "No\\\\", ) answers.set_color_by_tex_to_color_map({ "Yes" : GREEN, "No" : RED, }) bubble = ThoughtBubble() bubble.add_content(answers) bubble.resize_to_content() answers.shift(SMALL_BUFF*(RIGHT+UP)) bubble_group = VGroup(bubble, answers) bubble_group.scale(0.25) bubble_group.next_to(photon, UP+RIGHT, buff = 0) group = VGroup(photon, bubble_group) group.save_state() bubble_group.set_fill(opacity = 0) bubble_group.set_stroke(width = 0) self.play( group.restore, group.scale, 4, group.to_corner, DOWN + RIGHT, ) self.play(photon.eyes.blink_anim()) self.wait() self.play( FadeOut(bubble_group), photon.restore, ) def put_all_photons_in_A(self): A, B, C = circles = self.venn_diagram[:3] A_group, B_group, C_group = [ VGroup(circle, circle.label) for circle in circles ] B_group.save_state() C_group.save_state() A.generate_target() A.target.scale(4) A.target.shift( (FRAME_Y_RADIUS-MED_LARGE_BUFF)*UP - \ A.target.get_top() ) A.label.generate_target() A.label.target.scale(2) A.label.target.next_to( A.target.point_from_proportion(0.1), UP+RIGHT, SMALL_BUFF ) self.play( B_group.fade, 1, C_group.fade, 1, MoveToTarget(A), MoveToTarget(A.label), FadeOut(self.photon_words), self.photons.set_height, 0.85*A.target.get_height(), self.photons.space_out_submobjects, 0.8, self.photons.move_to, A.target, ) self.wait() self.A_group = A_group self.B_group = B_group self.C_group = C_group def separate_by_B(self): A_group = self.A_group B_group = self.B_group photons = self.photons B = B_group[0] B.target, B.label.target = B_group.saved_state B.target.scale(4) B.target.move_to(A_group[0]) B.target.shift(self.A_to_B_vect) B.label.target.scale(2) B.label.target.next_to( B.target.point_from_proportion(0.55), LEFT, SMALL_BUFF ) B_center = B.target.get_center() photons.sort_submobjects( lambda p : get_norm(p-B_center) ) in_B = VGroup(*photons[:85]) out_of_B = VGroup(*photons[85:]) out_of_B.sort_submobjects(lambda p : np.dot(p, 2*UP+LEFT)) self.play( MoveToTarget(B), MoveToTarget(B.label), in_B.shift, 0.5*DOWN+0.2*LEFT, out_of_B.scale_in_place, 1./0.8, out_of_B.shift, 0.15*(UP+RIGHT), ) words1 = TextMobject("85 also \\\\", "pass ", "B") words1.set_color_by_tex("B", GREEN) words1.scale(0.8) words1.next_to(A_group, LEFT, LARGE_BUFF).shift(UP) arrow1 = Arrow( words1.get_right(), in_B.get_corner(UP+LEFT) + MED_LARGE_BUFF*(DOWN+RIGHT) ) arrow1.set_color(GREEN) words2 = TextMobject("15 blocked \\\\", "by ", "B") words2.set_color_by_tex("B", GREEN) words2.scale(0.8) words2.next_to(A_group, LEFT, MED_LARGE_BUFF, UP) arrow2 = Arrow(words2.get_right(), out_of_B[-1]) arrow2.set_color(RED) self.play( Write(words1, run_time = 1), ShowCreation(arrow1), self.in_A_in_B.set_fill, GREEN, 0.5, Animation(in_B), ) self.wait() self.play( ReplacementTransform(words1, words2), ReplacementTransform(arrow1, arrow2), self.in_A_in_B.set_fill, None, 0, Animation(in_B), self.in_A_out_B.set_fill, RED, 0.5, Animation(out_of_B) ) self.wait() self.play(ApplyMethod( VGroup(self.in_A_out_B, out_of_B).shift, MED_LARGE_BUFF*UP, rate_func = wiggle, run_time = 1.5, )) self.wait(0.5) self.in_B = in_B self.out_of_B = out_of_B self.out_of_B_words = words2 self.out_of_B_arrow = arrow2 def separate_by_C(self): A_group = self.A_group B_group = self.B_group C_group = self.C_group in_B = self.in_B A, B, C = self.venn_diagram C.target, C.label.target = C_group.saved_state C.target.scale(4) C.target.move_to(A) C.target.shift(self.A_to_C_vect) C_center = C.target.get_center() C.label.target.scale(2) C.label.target.next_to( C.target.point_from_proportion(0), DOWN+RIGHT, buff = SMALL_BUFF ) in_B.sort_submobjects( lambda p : get_norm(p - C_center) ) in_C = VGroup(*in_B[:-11]) out_of_C = VGroup(*in_B[-11:]) in_C_out_B = VGroup(*self.out_of_B[:6]) words = TextMobject( "$15\\%$", "passing", "B \\\\", "get blocked by ", "C", ) words.scale(0.8) words.set_color_by_tex_to_color_map({ "B" : GREEN, "C" : BLUE, }) words.next_to(self.out_of_B_words, DOWN, LARGE_BUFF) words.to_edge(LEFT) percent = words[0] pound = TexMobject("\\#") pound.move_to(percent, RIGHT) less_than_15 = TexMobject("<15") less_than_15.next_to(words, DOWN) arrow = Arrow(words.get_right(), out_of_C) arrow.set_color(GREEN) C_copy = C.copy() C_copy.set_fill(BLACK, opacity = 1) self.play( self.in_A_in_B.set_fill, GREEN, 0.5, rate_func = there_and_back, ) self.play( MoveToTarget(C), MoveToTarget(C.label), in_C.shift, 0.2*DOWN+0.15*RIGHT, out_of_C.shift, SMALL_BUFF*(UP+LEFT), in_C_out_B.shift, 0.3*DOWN ) self.play( self.in_A_in_B_out_C.set_fill, GREEN, 0.5, Write(words, run_time = 1), ShowCreation(arrow), Animation(out_of_C), ) self.play(ApplyMethod( VGroup(self.in_A_in_B_out_C, out_of_C).shift, MED_LARGE_BUFF*UP, rate_func = wiggle )) self.wait() C.save_state() self.play(C.set_fill, BLACK, 1) self.wait() self.play(C.restore) self.wait(2) self.play(Transform(percent, pound)) self.play(Write(less_than_15, run_time = 1)) self.wait() self.in_C = in_C self.out_of_C = out_of_C words.add(less_than_15) self.out_of_C_words = words self.out_of_C_arrow = arrow def show_two_relevant_subsets(self): A, B, C = self.venn_diagram all_out_of_C = VGroup(*it.chain( self.out_of_B[6:], self.out_of_C, )) everything = VGroup(*self.get_top_level_mobjects()) photon_groups = [all_out_of_C, self.out_of_C, self.out_of_B] regions = [self.in_A_out_C, self.in_A_in_B_out_C, self.in_A_out_B] self.play(*[ ApplyMethod( m.scale, 0.7, method_kwargs = { "about_point" : FRAME_Y_RADIUS*DOWN } ) for m in everything ]) terms = VGroup( TexMobject("N(", "A", "\\checkmark", ",", "C", ")", "\\le"), TexMobject( "N(", "A", "\\checkmark", ",", "B", "\\checkmark", ",", "C", ")" ), TexMobject("+\\, N(", "A", "\\checkmark", ",", "B", ")"), ) terms.arrange_submobjects(RIGHT) terms.to_edge(UP) for term, index, group in zip(terms, [-3, -2, -2], photon_groups): term.set_color_by_tex("checkmark", "#00ff00") cross = Cross(term[index]) cross.set_color("#ff0000") cross.set_stroke(width = 8) term[index].add(cross) less_than = terms[0][-1] terms[0].remove(less_than) plus = terms[2][0][0] terms[2][0].remove(plus) rects = list(map(SurroundingRectangle, terms)) terms[2][0].add_to_back(plus) last_rects = VGroup(*rects[1:]) should_be_50 = TextMobject("Should be 50 \\\\", "...somehow") should_be_50.scale(0.8) should_be_50.next_to(rects[0], DOWN) lt_fifteen = VGroup(self.out_of_C_words[-1]).copy() something_lt_15 = TextMobject("(Something", "$<15$", ")") something_lt_15.scale(0.8) something_lt_15.next_to(rects[1], DOWN) lt_fifteen.target = something_lt_15 fifteen = VGroup(*self.out_of_B_words[0][:2]).copy() fifteen.generate_target() fifteen.target.scale(1.5) fifteen.target.next_to(rects[2], DOWN) nums = [should_be_50, lt_fifteen, fifteen] cross = Cross(less_than) cross.set_color("#ff0000") cross.set_stroke(width = 8) tweaser_group = VGroup( self.in_A_in_B_out_C.copy(), self.in_A_out_B.copy(), ) tweaser_group.set_fill(TEAL, 1) tweaser_group.set_stroke(TEAL, 5) #Fade out B circle faders = VGroup( B, B.label, self.out_of_B_words, self.out_of_C_words, self.out_of_B_arrow, self.out_of_C_arrow, *regions[1:] ) faders.save_state() self.play(faders.fade, 1) self.play(Write(terms[0]), run_time = 1) self.wait() self.photon_thinks_in_A_out_C() regions[0].set_stroke(YELLOW, width = 8) regions[0].set_fill(YELLOW, opacity = 0.25) self.play( VGroup(regions[0], all_out_of_C).shift, 0.5*UP, run_time = 1.5, rate_func = wiggle, ) self.wait(2) #Photons jump self.photons.save_state() self.play(Write(should_be_50[0], run_time = 1)) self.photons_jump_to_A_not_C_region() self.wait() self.play( faders.restore, self.photons.restore, ) self.play( faders.restore, regions[0].set_fill, None, 0, Animation(self.photons) ) #Funny business everything_copy = everything.copy().scale(1./3) braces = VGroup( Brace(everything_copy, LEFT), Brace(everything_copy, RIGHT), ).scale(3) funny_business = TextMobject("Funny business") funny_business.scale(1.5) funny_business.to_edge(UP) funny_business.shift(RIGHT) self.play( FadeIn(funny_business), *list(map(Write, braces)), run_time = 1 ) self.wait() self.play( FadeIn(less_than), *list(map(FadeOut, [funny_business, braces])) ) for term, group, region, num in zip(terms, photon_groups, regions, nums)[1:]: group.set_stroke(WHITE) self.play(Write(term, run_time = 1)) self.wait() self.play( ApplyMethod( VGroup(region, group).shift, 0.5*UP, rate_func = wiggle, run_time = 1.5, ), ) self.play(MoveToTarget(num)) self.wait() self.wait() self.play(ShowCreation(rects[0])) self.play( VGroup(regions[0], all_out_of_C).shift, 0.5*UP, run_time = 1.5, rate_func = wiggle, ) self.wait() self.play(Transform(rects[0], last_rects)) self.in_A_out_B.save_state() self.in_A_in_B_out_C.save_state() self.play( self.in_A_out_B.set_fill, YELLOW, 0.5, self.in_A_in_B_out_C.set_fill, YELLOW, 0.5, Animation(self.photons) ) self.wait() self.play( FadeOut(rects[0]), self.in_A_out_B.restore, self.in_A_in_B_out_C.restore, Animation(self.in_A_out_C), Animation(self.photons) ) self.wait() self.play( FadeIn(should_be_50[1]), ShowCreation(cross) ) morty = Mortimer() morty.to_corner(DOWN+RIGHT) contradiction = TextMobject("Contradiction!") contradiction.next_to(morty, UP, aligned_edge = RIGHT) contradiction.set_color(RED) self.play(FadeIn(morty)) self.play( morty.change, "confused", should_be_50, Write(contradiction, run_time = 1) ) self.play(Blink(morty)) self.wait() def photons_jump_to_A_not_C_region(self): in_C = self.in_C in_C.sort_submobjects(lambda p : np.dot(p, DOWN+RIGHT)) movers = VGroup(*self.in_C[:30]) for mover in movers: mover.generate_target() mover.target.shift(1.2*UP + 0.6*LEFT) mover.target.set_stroke(WHITE) self.play(LaggedStart( MoveToTarget, movers, path_arc = np.pi, lag_ratio = 0.3 )) def photon_thinks_in_A_out_C(self): photon = self.photons[-1] photon.save_state() photon.generate_target() photon.target.scale(4) photon.target.center().to_edge(LEFT).shift(DOWN) bubble = ThoughtBubble() content = TexMobject("A", "\\checkmark", ",", "C") content.set_color_by_tex("checkmark", "#00ff00") cross = Cross(content[-1]) cross.set_color("#ff0000") content.add(cross) bubble.add_content(content) bubble.resize_to_content() bubble.add(bubble.content) bubble.pin_to(photon.target).shift(SMALL_BUFF*RIGHT) bubble.save_state() bubble.scale(0.25) bubble.move_to(photon.get_corner(UP+RIGHT), DOWN+LEFT) bubble.fade() self.play( MoveToTarget(photon), bubble.restore, ) self.play(photon.eyes.blink_anim()) self.play( photon.restore, FadeOut(bubble) ) ####### def setup_venn_diagram_sections(self): in_A_out_B, in_A_in_B_out_C, in_A_out_C, in_A_in_B = segments = VGroup(*[ SVGMobject( file_name = "VennDiagram_" + s, stroke_width = 0, fill_opacity = 0.5, fill_color = YELLOW, ) for s in ("in_A_out_B", "in_A_in_B_out_C", "in_A_out_C", "in_A_in_B") ]) in_A_out_B.scale(2.59) in_A_out_B.move_to(3.74*UP + 2.97*RIGHT, UP+RIGHT) in_A_in_B_out_C.scale(1.84) in_A_in_B_out_C.move_to(2.23*UP, UP+RIGHT) in_A_out_C.scale(2.56) in_A_out_C.move_to(3*LEFT + (3.69)*UP, UP+LEFT) in_A_in_B.scale(2.24) in_A_in_B.move_to(2.23*UP + 3*LEFT, UP+LEFT) segments.set_fill(BLACK, opacity = 0) self.in_A_out_B = in_A_out_B self.in_A_in_B_out_C = in_A_in_B_out_C self.in_A_out_C = in_A_out_C self.in_A_in_B = in_A_in_B self.A_segments = segments def arrange_photons_in_circle(self, photons): R = np.sqrt(len(photons) / np.pi) pairs = [] rejected = [] for x, y in it.product(*[list(range(-int(R)-1, int(R)+2))]*2): if x**2 + y**2 < R**2: pairs.append((x, y)) else: rejected.append((x, y)) rejected.sort( kay=lambda x, y: (x**2 + y**2) ) for i in range(len(photons) - len(pairs)): pairs.append(rejected.pop()) for photon, (x, y) in zip(photons, pairs): photon.set_width(0.7) photon.move_to(x*RIGHT + y*UP) return photons class PonderingPiCreature(Scene): def construct(self): randy = Randolph() randy.to_edge(DOWN).shift(3*LEFT) self.play(randy.change, "pondering", UP+RIGHT) self.play(Blink(randy)) self.wait(2) self.play(Blink(randy)) self.wait(2) class ReEmphasizeVennDiagram(VennDiagramProofByContradiction): def construct(self): self.draw_venn_diagram(send_to_corner = False) self.rescale_diagram() self.setup_faded_circles() self.shift_B_circle() self.shift_C_circle() self.show_A_not_C_region() self.shorten_labels() self.show_inequality_with_circle() # self.emphasize_containment() self.write_50_percent() self.write_assumption() self.adjust_circles() def rescale_diagram(self): group = VGroup(self.venn_diagram, *[ c.label for c in self.venn_diagram ]) self.play( group.scale, 0.7, group.to_edge, DOWN, MED_SMALL_BUFF, ) self.clear() self.add_foreground_mobjects(*group) def setup_faded_circles(self): self.circles = self.venn_diagram[:3] self.black_circles = VGroup(*[ circ.copy().set_stroke(width = 0).set_fill(BLACK, 1) for circ in self.circles ]) self.filled_circles = VGroup(*[ circ.copy().set_stroke(width = 0).set_fill(circ.get_color(), 1) for circ in self.circles ]) def shift_B_circle(self): A, B, C = self.circles A0, B0, C0 = self.black_circles A1, B1, C1 = self.filled_circles words = TextMobject("Should be 15\\% \\\\ of circle ", "A") words.scale(0.7) words.set_color_by_tex("A", RED) words.next_to(A, UP, LARGE_BUFF) words.shift(RIGHT) arrow = Arrow( words.get_bottom(), A.get_top() + MED_SMALL_BUFF*RIGHT, color = RED ) self.play(FadeIn(A1)) self.play(FadeIn(B0)) self.play( FadeIn(words, submobject_mode = "lagged_start"), ShowCreation(arrow) ) self.wait() vect = 0.6*(A.get_center() - B.get_center()) self.play( B0.shift, vect, B.shift, vect, B.label.shift, vect, run_time = 2, rate_func = running_start, ) B1.shift(vect) self.wait() self.in_A_out_B_words = words self.in_A_out_B_arrow = arrow for mob in words, arrow: mob.save_state() def shift_C_circle(self): A, B, C = self.circles A0, B0, C0 = self.black_circles A1, B1, C1 = self.filled_circles words = TextMobject("Should be 15\\% \\\\ of circle ", "B") words.scale(0.7) words.set_color_by_tex("B", GREEN) words.next_to(B, LEFT) words.shift(2.5*UP) arrow = Arrow( words.get_bottom(), B.point_from_proportion(0.4), color = GREEN ) self.play( FadeOut(A1), FadeOut(B0), self.in_A_out_B_words.fade, 1, self.in_A_out_B_arrow.fade, 1, FadeIn(B1), FadeIn(words, submobject_mode = "lagged_start"), ShowCreation(arrow) ) self.play(FadeIn(C0)) self.wait(2) vect = 0.5*(B.get_center() - C.get_center()) self.play( C0.shift, vect, C.shift, vect, C.label.shift, vect, run_time = 2, rate_func = running_start, ) C1.shift(vect) self.wait() for mob in words, arrow: mob.save_state() self.in_B_out_C_words = words self.in_B_out_C_arrow = arrow def show_A_not_C_region(self): A, B, C = self.circles A0, B0, C0 = self.black_circles A1, B1, C1 = self.filled_circles A1_yellow_copy = A1.copy().set_fill(YELLOW) self.play( FadeOut(B1), FadeOut(C0), self.in_B_out_C_words.fade, 1, self.in_B_out_C_arrow.fade, 1, FadeIn(A1_yellow_copy) ) self.play(FadeIn(C0)) self.wait() self.A1_yellow_copy = A1_yellow_copy def shorten_labels(self): A, B, C = self.circles A0, B0, C0 = self.black_circles A1, B1, C1 = self.filled_circles for circle in A, B, C: circle.pre_label = VGroup(*circle.label[:-1]) circle.letter = circle.label[-1] self.play( A.pre_label.fade, 1, A.letter.scale, 2, A.letter.move_to, A.pre_label, LEFT, B.pre_label.fade, 1, B.letter.scale, 2, B.letter.get_right(), C.pre_label.fade, 1, C.letter.scale, 2, C.letter.move_to, C.pre_label, LEFT, C.letter.shift, DOWN+0.5*LEFT, ) for circle in A, B, C: circle.remove(circle.label) self.remove(circle.label) circle.add(circle.letter) self.add(circle.letter) def show_inequality_with_circle(self): A, B, C = self.circles A0, B0, C0 = self.black_circles A1, B1, C1 = self.filled_circles A1_yellow_copy = self.A1_yellow_copy inequality = VGroup( TexMobject("N(", "A", "\\checkmark", ",", "C", ")"), TexMobject("N(", "B", "\\checkmark", ",", "C", ")"), TexMobject("N(", "A", "\\checkmark", ",", "B", ")"), ) inequality.arrange_submobjects(RIGHT) for tex in inequality: tex.set_color_by_tex("checkmark", "#00ff00") if len(tex) > 1: cross = Cross(tex[-2], color = "#ff0000") cross.set_stroke(width = 8) tex[-2].add(cross) inequality.space_out_submobjects(2.1) big_le = TexMobject("\\le").scale(2) big_plus = TexMobject("+").scale(2) big_le.move_to(2.75*LEFT) big_plus.move_to(2.25*RIGHT) groups = VGroup(*[ VGroup( m2.copy(), m1.copy(), VGroup(*self.circles).copy() ) for m1, m2 in (C0, A1_yellow_copy), (C0, B1), (B0, A1) ]) for group, vect in zip(groups[1:], [UP, 5*RIGHT+UP]): group.scale_in_place(0.5) group.shift(vect) group.save_state() group.shift(-vect[0]*RIGHT + 5*LEFT) inequality.shift(2.25*DOWN + 0.25*LEFT) self.in_B_out_C_words.restore() self.in_B_out_C_words.move_to(2*UP) self.in_A_out_B_words.restore() self.in_A_out_B_words.move_to(5*RIGHT+2*UP) self.clear() self.play( groups[0].scale_in_place, 0.5, groups[0].shift, 5*LEFT + UP, Write(inequality[0], run_time = 1), FadeIn(big_le), ) self.wait() self.play(FadeIn(groups[1])) self.play( groups[1].restore, FadeIn(inequality[1]), FadeIn(self.in_B_out_C_words), FadeIn(big_plus), ) self.play(FadeIn(groups[2])) self.play( groups[2].restore, FadeIn(inequality[2]), FadeIn(self.in_A_out_B_words), ) self.wait(2) self.groups = groups self.inequality = inequality def emphasize_containment(self): groups = self.groups c1, c2 = [VGroup(*group[:2]).copy() for group in groups[1:]] foreground = VGroup(groups[0][-1], *groups[1:]) rect = SurroundingRectangle(groups[0]) self.play(ShowCreation(rect)) self.play(FadeOut(rect)) self.play( ApplyMethod( c1.shift, 4*LEFT, path_arc = -np.pi/2, ), Animation(foreground) ) self.play( ApplyMethod( c2.shift, 8*LEFT, path_arc = -np.pi/2, ), Animation(c1), Animation(foreground), run_time = 1.5 ) self.play( FadeOut(c2), FadeOut(c1), Animation(foreground), run_time = 2 ) self.wait() def write_50_percent(self): words = TextMobject( "Should be 50\\% \\\\ of circle ", "A", "...somehow" ) words.scale(0.7) words.set_color_by_tex("A", RED) words.move_to(5*LEFT + 2*UP) self.play(Write(words)) self.wait() def write_assumption(self): words = TextMobject("Assume circles have the same size$^*$") words.scale(0.8) words.to_edge(UP) footnote = TextMobject(""" *If you prefer, you can avoid the need for that assumption by swapping the roles of A and C here and writing a second inequality for added constraint. """) footnote.scale(0.5) footnote.to_corner(DOWN+RIGHT) footnote.add(words[-1]) words.remove(words[-1]) self.footnote = footnote self.play(FadeIn(words)) def adjust_circles(self): groups = self.groups A_group = VGroup( groups[0][0], groups[2][0], groups[0][2][0], groups[1][2][0], groups[2][2][0], ) B_group = VGroup( groups[1][0], groups[2][1], groups[0][2][1], groups[1][2][1], groups[2][2][1], ) C_group = VGroup( groups[0][1], groups[1][1], groups[0][2][2], groups[1][2][2], groups[2][2][2], ) def center_of_mass(mob): return np.apply_along_axis(np.mean, 0, mob.points) movers = [A_group, B_group, C_group] A_ref, B_ref, C_ref = [g[4] for g in movers] B_center = center_of_mass(B_ref) B_to_A = center_of_mass(A_ref) - B_center B_to_C = center_of_mass(C_ref) - B_center A_freq = 1 C_freq = -0.7 self.time = 0 dt = 1 / self.camera.frame_rate def move_around(total_time): self.time t_range = list(range(int(total_time/dt))) for x in ProgressDisplay(t_range): self.time += dt new_B_to_A = rotate_vector(B_to_A, self.time*A_freq) new_B_to_C = rotate_vector(B_to_C, self.time*C_freq) A_group.shift(B_center + new_B_to_A - center_of_mass(A_ref)) C_group.shift(B_center + new_B_to_C - center_of_mass(C_ref)) self.wait(dt) move_around(3) self.add(self.footnote) move_around(1) self.remove(self.footnote) move_around(15) class NoFirstMeasurementPreferenceBasedOnDirection(ShowVariousFilterPairs): CONFIG = { "filter_x_coordinates" : [0, 0, 0], "pol_filter_configs" : [ {"filter_angle" : angle} for angle in (0, np.pi/8, np.pi/4) ], "lines_depth" : 1.2, "lines_shift_vect" : SMALL_BUFF*OUT, "n_lines" : 30, } def setup(self): DirectionOfPolarization.setup(self) self.remove(self.axes, self.em_wave) zs = [2.5, 0, -2.5] chars = "ABC" colors = [RED, GREEN, BLUE] for z, char, color, pf in zip(zs, chars, colors, self.pol_filters): pf.scale_in_place(0.7) pf.move_to(z*OUT) label = TextMobject(char) label.add_background_rectangle() label.set_color(color) label.scale(0.7) label.rotate(np.pi/2, RIGHT) label.rotate(-np.pi/2, OUT) label.next_to(pf.arrow_label, UP, SMALL_BUFF) pf.arrow_label.add(label) self.add_foreground_mobject(pf) def construct(self): self.reposition_camera() self.show_lines() def reposition_camera(self): words = TextMobject("No statistical preference") words.to_corner(UP+LEFT) words.rotate(np.pi/2, RIGHT) self.move_camera( theta = -0.6*np.pi, added_anims = list(it.chain(*[ [ pf.arrow_label.rotate, np.pi/2, OUT, pf.arrow_label.next_to, pf.arrow, OUT+RIGHT, SMALL_BUFF ] for pf in self.pol_filters ] + [[FadeIn(words)]])) ) def show_lines(self): all_pre_lines = VGroup() all_post_lines = VGroup() for pf in self.pol_filters: pre_lines = self.get_lines(None, pf) post_lines = self.get_lines(pf, None) VGroup( *random.sample(post_lines, self.n_lines/2) ).set_stroke(BLACK, 0) all_pre_lines.add(*pre_lines) all_post_lines.add(*post_lines) kwargs = { "rate_func" : None, "submobject_mode" : "all_at_once" } self.play(ShowCreation(all_pre_lines, **kwargs)) self.play( ShowCreation(all_post_lines, **kwargs), Animation(self.pol_filters), Animation(all_pre_lines), ) self.add_foreground_mobject(all_pre_lines) self.wait(7)