def construct(self): self.cycloid = Cycloid(end_theta = np.pi) self.cycloid.set_color(YELLOW) self.top = self.cycloid.get_top()[1] self.bottom = self.cycloid.get_bottom()[1]-1 self.generate_layers() self.generate_discrete_path() photon_run = self.photon_run_along_path( self.discrete_path, run_time = 1, rate_func = rush_into ) self.continuous_to_smooth() self.add(*self.layers) self.show_layer_variables() self.play(photon_run) self.play(ShowCreation(self.discrete_path)) self.isolate_bend_points() self.clear() self.add(*self.layers) self.show_main_equation() self.ask_continuous_question()
def construct(self): text = TextMobject("Light") text.to_edge(UP) self.play(ShimmerIn(text)) self.play(self.photon_run_along_path( Cycloid(), rate_func = None )) self.wait()
def construct(self): self.cycloid = Cycloid(end_theta=np.pi) self.cycloid.set_color(YELLOW) self.top = self.cycloid.get_top()[1] self.bottom = self.cycloid.get_bottom()[1] - 1 self.generate_layers() self.generate_discrete_path() photon_run = self.photon_run_along_path(self.discrete_path, run_time=1, rate_func=rush_into) self.continuous_to_smooth() self.add(*self.layers) self.show_layer_variables() self.play(photon_run) self.play(ShowCreation(self.discrete_path)) self.isolate_bend_points() self.clear() self.add(*self.layers) self.show_main_equation() self.ask_continuous_question()
def construct(self): r_range = np.arange(0.5, 2, 0.25) cycloids = Mobject(*[ Cycloid(radius=r, end_theta=2*np.pi) for r in r_range ]) lower_left = 2*DOWN+6*LEFT lines = Mobject(*[ Line( lower_left, lower_left+5*r*np.cos(np.arctan(r)) * RIGHT+2*r*np.sin(np.arctan(r))*UP ) for r in r_range ]) nl = NumberLine(numbers_with_elongated_ticks=[]) x_axis = nl.copy().shift(3*UP) y_axis = nl.copy().rotate(np.pi/2).shift(6*LEFT) t_axis = nl.copy().shift(2*DOWN) x_label = TexMobject("x") x_label.next_to(x_axis, DOWN) x_label.to_edge(RIGHT) y_label = TexMobject("y") y_label.next_to(y_axis, RIGHT) y_label.shift(2*DOWN) t_label = TexMobject("t") t_label.next_to(t_axis, UP) t_label.to_edge(RIGHT) theta_label = TexMobject("\\theta") theta_label.next_to(y_axis, RIGHT) theta_label.to_edge(UP) words = TextMobject("Boundary conditions?") words.next_to(lines, RIGHT) words.shift(2*UP) self.play(ShowCreation(x_axis), ShimmerIn(x_label)) self.play(ShowCreation(y_axis), ShimmerIn(y_label)) self.play(ShowCreation(cycloids)) self.wait() self.play( Transform(cycloids, lines), Transform(x_axis, t_axis), Transform(x_label, t_label), Transform(y_label, theta_label), run_time=2 ) self.wait() self.play(ShimmerIn(words)) self.wait()
class MultilayeredGlass(PhotonScene, ZoomedScene): CONFIG = { "num_discrete_layers": 5, "num_variables": 3, "top_color": BLUE_E, "bottom_color": BLUE_A, "zoomed_canvas_frame_shape": (5, 5), "square_color": GREEN_B, } def construct(self): self.cycloid = Cycloid(end_theta=np.pi) self.cycloid.set_color(YELLOW) self.top = self.cycloid.get_top()[1] self.bottom = self.cycloid.get_bottom()[1] - 1 self.generate_layers() self.generate_discrete_path() photon_run = self.photon_run_along_path(self.discrete_path, run_time=1, rate_func=rush_into) self.continuous_to_smooth() self.add(*self.layers) self.show_layer_variables() self.play(photon_run) self.play(ShowCreation(self.discrete_path)) self.isolate_bend_points() self.clear() self.add(*self.layers) self.show_main_equation() self.ask_continuous_question() def continuous_to_smooth(self): self.add(*self.layers) continuous = self.get_continuous_background() self.add(continuous) self.wait() self.play(ShowCreation(continuous, rate_func=lambda t: smooth(1 - t))) self.remove(continuous) self.wait() def get_continuous_background(self): glass = FilledRectangle( height=self.top - self.bottom, width=FRAME_WIDTH, ) glass.sort_points(lambda p: -p[1]) glass.shift((self.top - glass.get_top()[1]) * UP) glass.set_color_by_gradient(self.top_color, self.bottom_color) return glass def generate_layer_info(self): self.layer_thickness = float(self.top - self.bottom) / self.num_discrete_layers self.layer_tops = np.arange(self.top, self.bottom, -self.layer_thickness) top_rgb, bottom_rgb = [ np.array(Color(color).get_rgb()) for color in (self.top_color, self.bottom_color) ] epsilon = 1. / (self.num_discrete_layers - 1) self.layer_colors = [ Color(rgb=interpolate(top_rgb, bottom_rgb, alpha)) for alpha in np.arange(0, 1 + epsilon, epsilon) ] def generate_layers(self): self.generate_layer_info() def create_region(top, color): return Region(lambda x, y: (y < top) & (y > top - self.layer_thickness), color=color) self.layers = [ create_region(top, color) for top, color in zip(self.layer_tops, self.layer_colors) ] def generate_discrete_path(self): points = self.cycloid.points tops = list(self.layer_tops) tops.append(tops[-1] - self.layer_thickness) indices = [np.argmin(np.abs(points[:, 1] - top)) for top in tops] self.bend_points = points[indices[1:-1]] self.path_angles = [] self.discrete_path = Mobject1D(color=YELLOW, density=3 * DEFAULT_POINT_DENSITY_1D) for start, end in zip(indices, indices[1:]): start_point, end_point = points[start], points[end] self.discrete_path.add_line(start_point, end_point) self.path_angles.append( angle_of_vector(start_point - end_point) - np.pi / 2) self.discrete_path.add_line( points[end], FRAME_X_RADIUS * RIGHT + (self.layer_tops[-1] - 1) * UP) def show_layer_variables(self): layer_top_pairs = zip(self.layer_tops[:self.num_variables], self.layer_tops[1:]) v_equations = [] start_ys = [] end_ys = [] center_paths = [] braces = [] for (top1, top2), x in zip(layer_top_pairs, it.count(1)): eq_mob = TexMobject(["v_%d" % x, "=", "\sqrt{\phantom{y_1}}"], size="\\Large") midpoint = UP * (top1 + top2) / 2 eq_mob.shift(midpoint) v_eq = eq_mob.split() center_paths.append( Line(midpoint + FRAME_X_RADIUS * LEFT, midpoint + FRAME_X_RADIUS * RIGHT)) brace_endpoints = Mobject(Point(self.top * UP + x * RIGHT), Point(top2 * UP + x * RIGHT)) brace = Brace(brace_endpoints, RIGHT) start_y = TexMobject("y_%d" % x, size="\\Large") end_y = start_y.copy() start_y.next_to(brace, RIGHT) end_y.shift(v_eq[-1].get_center()) end_y.shift(0.2 * RIGHT) v_equations.append(v_eq) start_ys.append(start_y) end_ys.append(end_y) braces.append(brace) for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]): photon_run = self.photon_run_along_path(path, rate_func=None) self.play(ShimmerIn(v_eq[0]), photon_run, run_time=time) self.wait() for start_y, brace in zip(start_ys, braces): self.add(start_y) self.play(GrowFromCenter(brace)) self.wait() quads = zip(v_equations, start_ys, end_ys, braces) self.equations = [] for v_eq, start_y, end_y, brace in quads: self.remove(brace) self.play(ShowCreation(v_eq[1]), ShowCreation(v_eq[2]), Transform(start_y, end_y)) v_eq.append(start_y) self.equations.append(Mobject(*v_eq)) def isolate_bend_points(self): arc_radius = 0.1 self.activate_zooming() little_square = self.get_zoomed_camera_mobject() for index in range(3): bend_point = self.bend_points[index] line = Line(bend_point + DOWN, bend_point + UP, color=WHITE, density=self.zoom_factor * DEFAULT_POINT_DENSITY_1D) angle_arcs = [] for i, rotation in [(index, np.pi / 2), (index + 1, -np.pi / 2)]: arc = Arc(angle=self.path_angles[i]) arc.scale(arc_radius) arc.rotate(rotation) arc.shift(bend_point) angle_arcs.append(arc) thetas = [] for i in [index + 1, index + 2]: theta = TexMobject("\\theta_%d" % i) theta.scale(0.5 / self.zoom_factor) vert = UP if i == index + 1 else DOWN horiz = rotate_vector(vert, np.pi / 2) theta.next_to(Point(bend_point), horiz, buff=0.01) theta.shift(1.5 * arc_radius * vert) thetas.append(theta) figure_marks = [line] + angle_arcs + thetas self.play( ApplyMethod(little_square.shift, bend_point - little_square.get_center(), run_time=2)) self.play(*map(ShowCreation, figure_marks)) self.wait() equation_frame = little_square.copy() equation_frame.scale(0.5) equation_frame.shift( little_square.get_corner(UP+RIGHT) - \ equation_frame.get_corner(UP+RIGHT) ) equation_frame.scale_in_place(0.9) self.show_snells(index + 1, equation_frame) self.remove(*figure_marks) self.disactivate_zooming() def show_snells(self, index, frame): left_text, right_text = [ "\\dfrac{\\sin(\\theta_%d)}{\\phantom{\\sqrt{y_1}}}" % x for x in (index, index + 1) ] left, equals, right = TexMobject([left_text, "=", right_text]).split() vs = [] sqrt_ys = [] for x, numerator in [(index, left), (index + 1, right)]: v, sqrt_y = [ TexMobject(text, size="\\Large").next_to(numerator, DOWN) for text in ("v_%d" % x, "\\sqrt{y_%d}" % x) ] vs.append(v) sqrt_ys.append(sqrt_y) start, end = [ Mobject(left.copy(), mobs[0], equals.copy(), right.copy(), mobs[1]).replace(frame) for mobs in (vs, sqrt_ys) ] self.add(start) self.wait(2) self.play(Transform(start, end, path_func=counterclockwise_path())) self.wait(2) self.remove(start, end) def show_main_equation(self): self.equation = TexMobject(""" \\dfrac{\\sin(\\theta)}{\\sqrt{y}} = \\text{constant} """) self.equation.shift(LEFT) self.equation.shift( (self.layer_tops[0] - self.equation.get_top()) * UP) self.add(self.equation) self.wait() def ask_continuous_question(self): continuous = self.get_continuous_background() line = Line(UP, DOWN, density=self.zoom_factor * DEFAULT_POINT_DENSITY_1D) theta = TexMobject("\\theta") theta.scale(0.5 / self.zoom_factor) self.play(ShowCreation(continuous), Animation(self.equation)) self.remove(*self.layers) self.play(ShowCreation(self.cycloid)) self.activate_zooming() little_square = self.get_zoomed_camera_mobject() self.add(line) indices = np.arange(0, self.cycloid.get_num_points() - 1, 10) for index in indices: point = self.cycloid.points[index] next_point = self.cycloid.points[index + 1] angle = angle_of_vector(point - next_point) for mob in little_square, line: mob.shift(point - mob.get_center()) arc = Arc(angle - np.pi / 2, start_angle=np.pi / 2) arc.scale(0.1) arc.shift(point) self.add(arc) if angle > np.pi / 2 + np.pi / 6: vect_angle = interpolate(np.pi / 2, angle, 0.5) vect = rotate_vector(RIGHT, vect_angle) theta.center() theta.shift(point) theta.shift(0.15 * vect) self.add(theta) self.wait(self.frame_duration) self.remove(arc)
class MultilayeredGlass(PhotonScene, ZoomedScene): CONFIG = { "num_discrete_layers" : 5, "num_variables" : 3, "top_color" : BLUE_E, "bottom_color" : BLUE_A, "zoomed_canvas_frame_shape" : (5, 5), "square_color" : GREEN_B, } def construct(self): self.cycloid = Cycloid(end_theta = np.pi) self.cycloid.set_color(YELLOW) self.top = self.cycloid.get_top()[1] self.bottom = self.cycloid.get_bottom()[1]-1 self.generate_layers() self.generate_discrete_path() photon_run = self.photon_run_along_path( self.discrete_path, run_time = 1, rate_func = rush_into ) self.continuous_to_smooth() self.add(*self.layers) self.show_layer_variables() self.play(photon_run) self.play(ShowCreation(self.discrete_path)) self.isolate_bend_points() self.clear() self.add(*self.layers) self.show_main_equation() self.ask_continuous_question() def continuous_to_smooth(self): self.add(*self.layers) continuous = self.get_continuous_background() self.add(continuous) self.wait() self.play(ShowCreation( continuous, rate_func = lambda t : smooth(1-t) )) self.remove(continuous) self.wait() def get_continuous_background(self): glass = FilledRectangle( height = self.top-self.bottom, width = FRAME_WIDTH, ) glass.sort_points(lambda p : -p[1]) glass.shift((self.top-glass.get_top()[1])*UP) glass.set_color_by_gradient(self.top_color, self.bottom_color) return glass def generate_layer_info(self): self.layer_thickness = float(self.top-self.bottom)/self.num_discrete_layers self.layer_tops = np.arange( self.top, self.bottom, -self.layer_thickness ) top_rgb, bottom_rgb = [ np.array(Color(color).get_rgb()) for color in self.top_color, self.bottom_color ] epsilon = 1./(self.num_discrete_layers-1) self.layer_colors = [ Color(rgb = interpolate(top_rgb, bottom_rgb, alpha)) for alpha in np.arange(0, 1+epsilon, epsilon) ] def generate_layers(self): self.generate_layer_info() def create_region(top, color): return Region( lambda x, y : (y < top) & (y > top-self.layer_thickness), color = color ) self.layers = [ create_region(top, color) for top, color in zip(self.layer_tops, self.layer_colors) ] def generate_discrete_path(self): points = self.cycloid.points tops = list(self.layer_tops) tops.append(tops[-1]-self.layer_thickness) indices = [ np.argmin(np.abs(points[:, 1]-top)) for top in tops ] self.bend_points = points[indices[1:-1]] self.path_angles = [] self.discrete_path = Mobject1D( color = YELLOW, density = 3*DEFAULT_POINT_DENSITY_1D ) for start, end in zip(indices, indices[1:]): start_point, end_point = points[start], points[end] self.discrete_path.add_line( start_point, end_point ) self.path_angles.append( angle_of_vector(start_point-end_point)-np.pi/2 ) self.discrete_path.add_line( points[end], FRAME_X_RADIUS*RIGHT+(self.layer_tops[-1]-1)*UP ) def show_layer_variables(self): layer_top_pairs = zip( self.layer_tops[:self.num_variables], self.layer_tops[1:] ) v_equations = [] start_ys = [] end_ys = [] center_paths = [] braces = [] for (top1, top2), x in zip(layer_top_pairs, it.count(1)): eq_mob = TexMobject( ["v_%d"%x, "=", "\sqrt{\phantom{y_1}}"], size = "\\Large" ) midpoint = UP*(top1+top2)/2 eq_mob.shift(midpoint) v_eq = eq_mob.split() center_paths.append(Line( midpoint+FRAME_X_RADIUS*LEFT, midpoint+FRAME_X_RADIUS*RIGHT )) brace_endpoints = Mobject( Point(self.top*UP+x*RIGHT), Point(top2*UP+x*RIGHT) ) brace = Brace(brace_endpoints, RIGHT) start_y = TexMobject("y_%d"%x, size = "\\Large") end_y = start_y.copy() start_y.next_to(brace, RIGHT) end_y.shift(v_eq[-1].get_center()) end_y.shift(0.2*RIGHT) v_equations.append(v_eq) start_ys.append(start_y) end_ys.append(end_y) braces.append(brace) for v_eq, path, time in zip(v_equations, center_paths, [2, 1, 0.5]): photon_run = self.photon_run_along_path( path, rate_func = None ) self.play( ShimmerIn(v_eq[0]), photon_run, run_time = time ) self.wait() for start_y, brace in zip(start_ys, braces): self.add(start_y) self.play(GrowFromCenter(brace)) self.wait() quads = zip(v_equations, start_ys, end_ys, braces) self.equations = [] for v_eq, start_y, end_y, brace in quads: self.remove(brace) self.play( ShowCreation(v_eq[1]), ShowCreation(v_eq[2]), Transform(start_y, end_y) ) v_eq.append(start_y) self.equations.append(Mobject(*v_eq)) def isolate_bend_points(self): arc_radius = 0.1 self.activate_zooming() little_square = self.get_zoomed_camera_mobject() for index in range(3): bend_point = self.bend_points[index] line = Line( bend_point+DOWN, bend_point+UP, color = WHITE, density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D ) angle_arcs = [] for i, rotation in [(index, np.pi/2), (index+1, -np.pi/2)]: arc = Arc(angle = self.path_angles[i]) arc.scale(arc_radius) arc.rotate(rotation) arc.shift(bend_point) angle_arcs.append(arc) thetas = [] for i in [index+1, index+2]: theta = TexMobject("\\theta_%d"%i) theta.scale(0.5/self.zoom_factor) vert = UP if i == index+1 else DOWN horiz = rotate_vector(vert, np.pi/2) theta.next_to( Point(bend_point), horiz, buff = 0.01 ) theta.shift(1.5*arc_radius*vert) thetas.append(theta) figure_marks = [line] + angle_arcs + thetas self.play(ApplyMethod( little_square.shift, bend_point - little_square.get_center(), run_time = 2 )) self.play(*map(ShowCreation, figure_marks)) self.wait() equation_frame = little_square.copy() equation_frame.scale(0.5) equation_frame.shift( little_square.get_corner(UP+RIGHT) - \ equation_frame.get_corner(UP+RIGHT) ) equation_frame.scale_in_place(0.9) self.show_snells(index+1, equation_frame) self.remove(*figure_marks) self.disactivate_zooming() def show_snells(self, index, frame): left_text, right_text = [ "\\dfrac{\\sin(\\theta_%d)}{\\phantom{\\sqrt{y_1}}}"%x for x in index, index+1 ] left, equals, right = TexMobject( [left_text, "=", right_text] ).split() vs = [] sqrt_ys = [] for x, numerator in [(index, left), (index+1, right)]: v, sqrt_y = [ TexMobject( text, size = "\\Large" ).next_to(numerator, DOWN) for text in "v_%d"%x, "\\sqrt{y_%d}"%x ] vs.append(v) sqrt_ys.append(sqrt_y) start, end = [ Mobject( left.copy(), mobs[0], equals.copy(), right.copy(), mobs[1] ).replace(frame) for mobs in vs, sqrt_ys ] self.add(start) self.wait(2) self.play(Transform( start, end, path_func = counterclockwise_path() )) self.wait(2) self.remove(start, end) def show_main_equation(self): self.equation = TexMobject(""" \\dfrac{\\sin(\\theta)}{\\sqrt{y}} = \\text{constant} """) self.equation.shift(LEFT) self.equation.shift( (self.layer_tops[0]-self.equation.get_top())*UP ) self.add(self.equation) self.wait() def ask_continuous_question(self): continuous = self.get_continuous_background() line = Line( UP, DOWN, density = self.zoom_factor*DEFAULT_POINT_DENSITY_1D ) theta = TexMobject("\\theta") theta.scale(0.5/self.zoom_factor) self.play( ShowCreation(continuous), Animation(self.equation) ) self.remove(*self.layers) self.play(ShowCreation(self.cycloid)) self.activate_zooming() little_square = self.get_zoomed_camera_mobject() self.add(line) indices = np.arange( 0, self.cycloid.get_num_points()-1, 10 ) for index in indices: point = self.cycloid.points[index] next_point = self.cycloid.points[index+1] angle = angle_of_vector(point - next_point) for mob in little_square, line: mob.shift(point - mob.get_center()) arc = Arc(angle-np.pi/2, start_angle = np.pi/2) arc.scale(0.1) arc.shift(point) self.add(arc) if angle > np.pi/2 + np.pi/6: vect_angle = interpolate(np.pi/2, angle, 0.5) vect = rotate_vector(RIGHT, vect_angle) theta.center() theta.shift(point) theta.shift(0.15*vect) self.add(theta) self.wait(self.frame_duration) self.remove(arc)