diff --git a/bell.py b/bell.py index b2a811c5..3d17e2c4 100644 --- a/bell.py +++ b/bell.py @@ -192,21 +192,28 @@ class DirectionOfPolarization(FilterScene): about_point = self.em_wave.start_point, ) if self.apply_filter: - filter_x = self.pol_filter.get_center()[0] - for vect_group, angle in (self.em_wave.E_vects, 0), (self.em_wave.M_vects, np.pi/2): - proj_vect = rotate_vector( - OUT, self.pol_filter.filter_angle + angle, RIGHT, + filters = sorted( + self.pol_filters, + lambda pf1, pf2 : cmp( + pf1.get_center()[0], + pf2.get_center()[0], ) - 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() - if start[0] > filter_x: - vect.apply_matrix(proj_matrix) - vect.shift(start - vect.get_start()) - + ) + for pol_filter in filters: + filter_x = pol_filter.get_center()[0] + for vect_group, angle in (self.em_wave.E_vects, 0), (self.em_wave.M_vects, 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() + if start[0] > filter_x: + vect.apply_matrix(proj_matrix) + vect.shift(start - vect.get_start()) class PhotonsThroughPerpendicularFilters(DirectionOfPolarization): CONFIG = { @@ -258,7 +265,7 @@ class PhotonsThroughPerpendicularFilters(DirectionOfPolarization): em_wave = self.em_wave.copy(), run_time = 1.5, ) - for x in 2, -2, 10 + for x in -2, 2, 10 ] def get_probability_text(self, prob = 0): @@ -288,6 +295,57 @@ class PhotonsThroughPerpendicularFilters(DirectionOfPolarization): return prob_text +class MoreFiltersMoreLight(FilterScene): + CONFIG = { + "filter_x_coordinates" : 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, + } + def construct(self): + self.remove(self.axes) + pfs = self.pol_filters + for pf in pfs: + pf.set_fill(WHITE, opacity = 0.25) + pf.arrow.set_fill(opacity = 1) + turn_off_3d_shading(pfs) + self.remove(pfs) + self.add(pfs[4], pfs[2], pfs[0]) + + self.move_camera( + phi = 0.9*np.pi/2, + theta = -0.95*np.pi, + ) + self.dither() + for i in 1, 3: + pf = pfs[i] + foreground = VGroup(*reversed(pfs[:i])) + pf.save_state() + pf.shift(6*OUT) + self.remove(foreground) + self.play( + pf.restore, + Animation(foreground), + run_time = 2 + ) + self.dither() + +class ConfusedPiCreature(Scene): + def construct(self): + randy = Randolph() + self.play( + randy.change, "confused", 3*(UP+RIGHT), + ) + self.play(Blink(randy)) + self.dither(2) + self.play(Blink(randy)) + self.dither(2) + class AngryPiCreature(PiCreatureScene): def construct(self): self.pi_creature_says( @@ -490,7 +548,7 @@ class ShowVariousFilterPairs(PhotonsThroughPerpendicularFilters): def show_photons(self, n_photons = 5): p = self.get_prob() photons = [ - copy.deepcopy(self.photons[2 if random.random() < p else 0]) + copy.deepcopy(self.photons[2 if random.random() < p else 1]) for x in range(n_photons) ] for photon in photons: @@ -514,7 +572,6 @@ class ShowVariousFilterPairs(PhotonsThroughPerpendicularFilters): def get_prob(self): return np.cos(self.second_filter.filter_angle)**2 - class ForgetPreviousActions(PhotonsThroughPerpendicularFilters): CONFIG = { "filter_x_coordinates" : [-6, -2, 2], @@ -525,20 +582,271 @@ class ForgetPreviousActions(PhotonsThroughPerpendicularFilters): "start_theta" : -0.6*np.pi } def construct(self): + self.photons = self.get_photons()[1:] + for pf in self.pol_filters: pf.arrow_label.rotate_in_place(np.pi/2, OUT) pf.arrow_label.next_to(pf.arrow, RIGHT) - rect = SurroundingRectangle(VGroup(*self.pol_filters[1:])) - rect.rotate_in_place(np.pi/2, RIGHT) - rect.stretch_ - words = TextMobject( + group = VGroup(*self.pol_filters[1:]) + 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" ) - words.rotate(np.pi/2, RIGHT) - words.next_to(rect, OUT) - self.add(rect, words) + prob_words.add_background_rectangle() + prob_words.rotate(np.pi/2, RIGHT) + prob_words.next_to(rect1, OUT) + + self.add(rect1) + self.play(Write(prob_words)) + for x in range(2): + self.shoot_photon() + + rect2 = SurroundingRectangle(self.pol_filter, color = RED) + rect2.rotate_in_place(np.pi/2, RIGHT) + rect2.rescale_to_fit(self.pol_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) + ) + for x in range(4): + self.shoot_photon() + + + def shoot_photon(self): + photon = random.choice(self.photons) + added_anims = [] + if photon.filter_distance == SPACE_WIDTH + 2: + added_anims.append(self.get_filter_absorbtion_animation( + self.pol_filters[2], photon + )) + self.play(photon, *added_anims, run_time = 1.5) + +class NumbersSuggestHiddenVariablesAreImpossible(TeacherStudentsScene): + def construct(self): + self.teacher_says( + "These numbers suggest\\\\", + "no hidden variables" + ) + self.change_student_modes("erm", "sassy", "confused") + self.dither(3) + +class VennDiagramProofByContradiction(Scene): + CONFIG = { + "circle_colors" : [RED, GREEN, BLUE] + } + def construct(self): + # self.force_skipping() + + 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() + self.show_real_value() + self.contradiction() + + def draw_venn_diagram(self): + venn_diagrom = 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) + ) + ]) + venn_diagrom.center() + props = [1./12, 0.5, 0] + for circle, char, prop in zip(venn_diagrom, "ABC", props): + label = TextMobject("Would pass \\\\ through", char) + label.highlight_by_tex(char, circle.get_color()) + center = circle.get_center() + label.move_to(center) + label.generate_target() + point = circle.point_from_proportion(prop) + label.target.next_to(point, point-center, SMALL_BUFF) + + circle.label = label + + last_circle = None + for circle in venn_diagrom: + 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.dither() + + venn_diagrom.add(*[c.label for c in venn_diagrom]) + self.venn_diagrom = venn_diagrom + for part in self.venn_diagrom: + part.save_state() + + self.play( + self.venn_diagrom.scale, 0.25, + self.venn_diagrom.to_corner, UP+RIGHT + ) + + 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 + ) + 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.scale_to_fit_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[-1] + photon.save_state() + photon.generate_target() + photon.target.scale(4) + photon.target.next_to(self.photons, RIGHT) + + answers = TextMobject( + "Pass through A?", "Yes\\\\", + "Pass through B?", "No\\\\", + "Pass through C?", "No\\\\", + ) + answers.highlight_by_tex_to_color_map({ + "Yes" : GREEN, + "No" : RED, + }) + answers.next_to(photon.target, RIGHT) + + self.play( + MoveToTarget(photon), + FadeIn(answers) + ) + self.dither(2) + self.play( + FadeOut(answers), + photon.restore, + ) + + def put_all_photons_in_A(self): + A_circle, B_circle, C_circle = circles = self.venn_diagrom[:3] + A_group, B_group, C_group = [ + VGroup(circle, circle.label) + for circle in circles + ] + B_group.save_state() + C_group.save_state() + + A_group.generate_target() + A_group.target.scale(4) + A_group.target.center().to_edge(UP) + + self.play( + B_group.fade, 1, + C_group.fade, 1, + MoveToTarget(A_group), + FadeOut(self.photon_words), + self.photons.scale_to_fit_height, + 0.85*A_group.target.get_height(), + self.photons.move_to, A_group.target[0].get_center(), + ) + self.dither() + + self.A_group = A_group + self.B_group = B_group + self.C_group = C_group + + def separate_by_B(self): + pass + + def separate_by_C(self): + pass + + def show_two_relevant_subsets(self): + pass + + def show_real_value(self): + pass + + def contradiction(self): + pass + + ####### + + def arrange_photons_in_circle(self, photons): + R = np.sqrt(len(photons) / np.pi) + pairs = [] + rejected = [] + for x, y in it.product(*[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( + lambda (x1, y1), (x2, y2) : (x2**2 + y2**2) - (x1**2 + y1**2) + ) + for i in range(len(photons) - len(pairs)): + pairs.append(rejected.pop()) + for photon, (x, y) in zip(photons, pairs): + photon.scale_to_fit_width(0.7) + photon.move_to(x*RIGHT + y*UP) + return photons + + + + + diff --git a/mobject/mobject.py b/mobject/mobject.py index 4c3ed6cf..f1499d77 100644 --- a/mobject/mobject.py +++ b/mobject/mobject.py @@ -542,6 +542,12 @@ class Mobject(object): def get_left(self): return self.get_edge_center(LEFT) + def get_zenith(self): + return self.get_edge_center(OUT) + + def get_nadir(self): + return self.get_edge_center(IN) + def length_over_dim(self, dim): return ( self.reduce_across_dimension(np.max, np.max, dim) - @@ -554,6 +560,9 @@ class Mobject(object): def get_height(self): return self.length_over_dim(1) + def get_depth(self): + return self.length_over_dim(2) + def point_from_proportion(self, alpha): raise Exception("Not implemented") @@ -591,6 +600,25 @@ class Mobject(object): self.center() return self + def arrange_submobjects_in_grid(self, n_rows = None, n_cols = None, **kwargs): + submobs = self.submobjects + if n_rows is None and n_cols is None: + n_cols = int(np.sqrt(len(submobs))) + + if n_rows is not None: + v1 = RIGHT + v2 = DOWN + n = n_rows + elif n_cols is not None: + v1 = DOWN + v2 = RIGHT + n = n_cols + Group(*[ + Group(*submobs[i:i+n]).arrange_submobjects(v1, **kwargs) + for i in range(0, len(submobs), n) + ]).arrange_submobjects(v2, **kwargs) + return self + def sort_submobjects(self, point_to_num_func = lambda p : p[0]): self.submobjects.sort( lambda *mobs : cmp(*[ diff --git a/topics/three_dimensions.py b/topics/three_dimensions.py index e9b9a1aa..46be2150 100644 --- a/topics/three_dimensions.py +++ b/topics/three_dimensions.py @@ -179,12 +179,16 @@ class ThreeDScene(Scene): ############## def should_shade_in_3d(mobject): - return hasattr(mobject, "shade_in_3d") + return hasattr(mobject, "shade_in_3d") and mobject.shade_in_3d def shade_in_3d(mobject): for submob in mobject.submobject_family(): submob.shade_in_3d = True +def turn_off_3d_shading(mobject): + for submob in mobject.submobject_family(): + submob.shade_in_3d = False + class ThreeDMobject(VMobject): def __init__(self, *args, **kwargs): VMobject.__init__(self, *args, **kwargs)