import numpy as np import itertools as it from helpers import * from mobject.tex_mobject import TexMobject, TextMobject, Brace from mobject import Mobject, Mobject1D from mobject.image_mobject import \ ImageMobject, MobjectFromPixelArray from topics.three_dimensions import Stars from animation import Animation from animation.transform import * from animation.simple_animations import * from animation.playground import TurnInsideOut, Vibrate from topics.geometry import * from topics.characters import Randolph, Mathematician from topics.functions import * from topics.number_line import * from mobject.region import Region, region_from_polygon_vertices from scene import Scene from scene.zoomed_scene import ZoomedScene from brachistochrone.curves import \ Cycloid, PathSlidingScene, RANDY_SCALE_FACTOR, TryManyPaths class Lens(Arc): CONFIG = { "radius" : 2, "angle" : np.pi/2, "color" : BLUE_B, } def __init__(self, **kwargs): digest_config(self, kwargs) Arc.__init__(self, self.angle, **kwargs) def generate_points(self): Arc.generate_points(self) self.rotate(-np.pi/4) self.shift(-self.get_left()) self.add_points(self.copy().rotate(np.pi).points) class PhotonScene(Scene): def wavify(self, mobject): result = mobject.copy() result.ingest_submobjects() tangent_vectors = result.points[1:]-result.points[:-1] lengths = np.apply_along_axis( np.linalg.norm, 1, tangent_vectors ) thick_lengths = lengths.repeat(3).reshape((len(lengths), 3)) unit_tangent_vectors = tangent_vectors/thick_lengths rot_matrix = np.transpose(rotation_matrix(np.pi/2, OUT)) normal_vectors = np.dot(unit_tangent_vectors, rot_matrix) # total_length = np.sum(lengths) times = np.cumsum(lengths) nudge_sizes = 0.1*np.sin(2*np.pi*times) thick_nudge_sizes = nudge_sizes.repeat(3).reshape((len(nudge_sizes), 3)) nudges = thick_nudge_sizes*normal_vectors result.points[1:] += nudges return result def photon_run_along_path(self, path, color = YELLOW, **kwargs): if "rate_func" not in kwargs: kwargs["rate_func"] = None photon = self.wavify(path) photon.highlight(color) return ShowPassingFlash(photon, **kwargs) class SimplePhoton(PhotonScene): 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.dither() class MultipathPhotonScene(PhotonScene): CONFIG = { "num_paths" : 5 } def run_along_paths(self, **kwargs): paths = self.get_paths() colors = Color(YELLOW).range_to(WHITE, len(paths)) for path, color in zip(paths, colors): path.highlight(color) photon_runs = [ self.photon_run_along_path(path) for path in paths ] for photon_run, path in zip(photon_runs, paths): self.play( photon_run, ShowCreation( path, rate_func = lambda t : 0.9*smooth(t) ), **kwargs ) self.dither() def generate_paths(self): raise Exception("Not Implemented") class PhotonThroughLens(MultipathPhotonScene): def construct(self): self.lens = Lens() self.add(self.lens) self.run_along_paths() def get_paths(self): interval_values = np.arange(self.num_paths).astype('float') interval_values /= (self.num_paths-1.) first_contact = [ self.lens.point_from_proportion(0.4*v+0.55) for v in reversed(interval_values) ] second_contact = [ self.lens.point_from_proportion(0.3*v + 0.1) for v in interval_values ] focal_point = 2*RIGHT return [ Mobject( Line(SPACE_WIDTH*LEFT + fc[1]*UP, fc), Line(fc, sc), Line(sc, focal_point), Line(focal_point, 6*focal_point-5*sc) ).ingest_submobjects() for fc, sc in zip(first_contact, second_contact) ] class TransitionToOptics(PhotonThroughLens): def construct(self): optics = TextMobject("Optics") optics.to_edge(UP) self.add(optics) self.has_started = False PhotonThroughLens.construct(self) def play(self, *args, **kwargs): if not self.has_started: self.has_started = True everything = Mobject(*self.mobjects) vect = 2*SPACE_WIDTH*RIGHT everything.shift(vect) self.play(ApplyMethod( everything.shift, -vect, rate_func = rush_from )) Scene.play(self, *args, **kwargs) class PhotonOffMirror(MultipathPhotonScene): def construct(self): self.mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP])) self.mirror.highlight(GREY) self.add(self.mirror) self.run_along_paths() def get_paths(self): interval_values = np.arange(self.num_paths).astype('float') interval_values /= (self.num_paths-1) anchor_points = [ self.mirror.point_from_proportion(0.6*v+0.3) for v in interval_values ] start_point = 5*LEFT+3*UP end_points = [] for point in anchor_points: vect = start_point-point vect[1] *= -1 end_points.append(point+2*vect) return [ Mobject( Line(start_point, anchor_point), Line(anchor_point, end_point) ).ingest_submobjects() for anchor_point, end_point in zip(anchor_points, end_points) ] class PhotonsInWater(MultipathPhotonScene): def construct(self): water = Region(lambda x, y : y < 0, color = BLUE_E) self.add(water) self.run_along_paths() def get_paths(self): x, y = -3, 3 start_point = x*RIGHT + y*UP angles = np.arange(np.pi/18, np.pi/3, np.pi/18) midpoints = y*np.arctan(angles) end_points = midpoints + SPACE_HEIGHT*np.arctan(2*angles) return [ Mobject( Line(start_point, [midpoint, 0, 0]), Line([midpoint, 0, 0], [end_point, -SPACE_HEIGHT, 0]) ).ingest_submobjects() for midpoint, end_point in zip(midpoints, end_points) ] class ShowMultiplePathsScene(PhotonScene): def construct(self): text = TextMobject("Which path minimizes travel time?") text.to_edge(UP) self.generate_start_and_end_points() point_a = Dot(self.start_point) point_b = Dot(self.end_point) A = TextMobject("A").next_to(point_a, UP) B = TextMobject("B").next_to(point_b, DOWN) paths = self.get_paths() for point, letter in [(point_a, A), (point_b, B)]: self.play( ShowCreation(point), ShimmerIn(letter) ) self.play(ShimmerIn(text)) curr_path = paths[0].copy() curr_path_copy = curr_path.copy().ingest_submobjects() self.play( self.photon_run_along_path(curr_path), ShowCreation(curr_path_copy, rate_func = rush_into) ) self.remove(curr_path_copy) for path in paths[1:] + [paths[0]]: self.play(Transform(curr_path, path, run_time = 4)) self.dither() self.path = curr_path.ingest_submobjects() def generate_start_and_end_points(self): raise Exception("Not Implemented") def get_paths(self): raise Exception("Not implemented") class ShowMultiplePathsThroughLens(ShowMultiplePathsScene): def construct(self): self.lens = Lens() self.add(self.lens) ShowMultiplePathsScene.construct(self) def generate_start_and_end_points(self): self.start_point = 3*LEFT + UP self.end_point = 2*RIGHT def get_paths(self): alphas = [0.25, 0.4, 0.58, 0.75] lower_right, upper_right, upper_left, lower_left = map( self.lens.point_from_proportion, alphas ) return [ Mobject( Line(self.start_point, a), Line(a, b), Line(b, self.end_point) ).highlight(color) for (a, b), color in zip( [ (upper_left, upper_right), (upper_left, lower_right), (lower_left, lower_right), (lower_left, upper_right), ], Color(YELLOW).range_to(WHITE, 4) ) ] class ShowMultiplePathsOffMirror(ShowMultiplePathsScene): def construct(self): mirror = Line(*SPACE_HEIGHT*np.array([DOWN, UP])) mirror.highlight(GREY) self.add(mirror) ShowMultiplePathsScene.construct(self) def generate_start_and_end_points(self): self.start_point = 4*LEFT + 2*UP self.end_point = 4*LEFT + 2*DOWN def get_paths(self): return [ Mobject( Line(self.start_point, midpoint), Line(midpoint, self.end_point) ).highlight(color) for midpoint, color in zip( [2*UP, 2*DOWN], Color(YELLOW).range_to(WHITE, 2) ) ] class ShowMultiplePathsInWater(ShowMultiplePathsScene): def construct(self): glass = Region(lambda x, y : y < 0, color = BLUE_E) self.generate_start_and_end_points() straight = Line(self.start_point, self.end_point) slow = TextMobject("Slow") slow.rotate(np.arctan(straight.get_slope())) slow.shift(straight.points[int(0.7*straight.get_num_points())]) slow.shift(0.5*DOWN) too_long = TextMobject("Too long") too_long.shift(UP) air = TextMobject("Air").shift(2*UP) water = TextMobject("Water").shift(2*DOWN) self.add(glass) self.play(GrowFromCenter(air)) self.play(GrowFromCenter(water)) self.dither() self.remove(air, water) ShowMultiplePathsScene.construct(self) self.play( Transform(self.path, straight) ) self.dither() self.play(GrowFromCenter(slow)) self.dither() self.remove(slow) self.leftmost.ingest_submobjects() self.play(Transform(self.path, self.leftmost, run_time = 3)) self.dither() self.play(ShimmerIn(too_long)) self.dither() def generate_start_and_end_points(self): self.start_point = 3*LEFT + 2*UP self.end_point = 3*RIGHT + 2*DOWN def get_paths(self): self.leftmost, self.rightmost = result = [ Mobject( Line(self.start_point, midpoint), Line(midpoint, self.end_point) ).highlight(color) for midpoint, color in zip( [3*LEFT, 3*RIGHT], Color(YELLOW).range_to(WHITE, 2) ) ] return result class StraightLinesFastestInConstantMedium(PhotonScene): def construct(self): kwargs = {"size" : "\\Large"} left = TextMobject("Speed of light is constant", **kwargs) arrow = TexMobject("\\Rightarrow", **kwargs) right = TextMobject("Staight path is fastest", **kwargs) left.next_to(arrow, LEFT) right.next_to(arrow, RIGHT) squaggle, line = self.get_paths() self.play(*map(ShimmerIn, [left, arrow, right])) self.play(ShowCreation(squaggle)) self.play(self.photon_run_along_path( squaggle, run_time = 2, rate_func = None )) self.play(Transform( squaggle, line, path_func = path_along_arc(np.pi) )) self.play(self.photon_run_along_path(line, rate_func = None)) self.dither() def get_paths(self): squaggle = ParametricFunction( lambda t : (0.5*t+np.cos(t))*RIGHT+np.sin(t)*UP, start = -np.pi, end = 2*np.pi ) squaggle.shift(2*UP) start, end = squaggle.points[0], squaggle.points[-1] line = Line(start, end) result = [squaggle, line] for mob in result: mob.highlight(BLUE_D) return result class PhtonBendsInWater(PhotonScene, ZoomedScene): def construct(self): glass = Region(lambda x, y : y < 0, color = BLUE_E) kwargs = { "density" : self.zoom_factor*DEFAULT_POINT_DENSITY_1D } top_line = Line(SPACE_HEIGHT*UP+2*LEFT, ORIGIN, **kwargs) extension = Line(ORIGIN, SPACE_HEIGHT*DOWN+2*RIGHT, **kwargs) bottom_line = Line(ORIGIN, SPACE_HEIGHT*DOWN+RIGHT, **kwargs) path1 = Mobject(top_line, extension) path2 = Mobject(top_line, bottom_line) for mob in path1, path2: mob.ingest_submobjects() extension.highlight(RED) theta1 = np.arctan(bottom_line.get_slope()) theta2 = np.arctan(extension.get_slope()) arc = Arc(theta2-theta1, start_angle = theta1, radius = 2) question_mark = TextMobject("$\\theta$?") question_mark.shift(arc.get_center()+0.5*DOWN+0.25*RIGHT) wave = self.wavify(path2) wave.highlight(YELLOW) wave.scale(0.5) self.add(glass) self.play(ShowCreation(path1)) self.play(Transform(path1, path2)) self.dither() # self.activate_zooming() self.dither() self.play(ShowPassingFlash( wave, run_time = 3, rate_func = None )) self.dither() self.play(ShowCreation(extension)) self.play( ShowCreation(arc), ShimmerIn(question_mark) ) class LightIsFasterInAirThanWater(ShowMultiplePathsInWater): def construct(self): glass = Region(lambda x, y : y < 0, color = BLUE_E) equation = TexMobject("v_{\\text{air}} > v_{\\text{water}}") equation.to_edge(UP) path = Line(SPACE_WIDTH*LEFT, SPACE_WIDTH*RIGHT) path1 = path.copy().shift(2*UP) path2 = path.copy().shift(2*DOWN) self.add(glass) self.play(ShimmerIn(equation)) self.dither() photon_runs = [] photon_runs.append(self.photon_run_along_path( path1, rate_func = lambda t : min(1, 1.2*t) )) photon_runs.append(self.photon_run_along_path(path2)) self.play(*photon_runs, **{"run_time" : 2}) self.dither() class GeometryOfGlassSituation(ShowMultiplePathsInWater): def construct(self): glass = Region(lambda x, y : y < 0, color = BLUE_E) self.generate_start_and_end_points() left = self.start_point[0]*RIGHT right = self.end_point[0]*RIGHT start_x = interpolate(left, right, 0.2) end_x = interpolate(left, right, 1.0) left_line = Line(self.start_point, left, color = RED_D) right_line = Line(self.end_point, right, color = RED_D) h_1, h_2 = map(TexMobject, ["h_1", "h_2"]) h_1.next_to(left_line, LEFT) h_2.next_to(right_line, RIGHT) point_a = Dot(self.start_point) point_b = Dot(self.end_point) A = TextMobject("A").next_to(point_a, UP) B = TextMobject("B").next_to(point_b, DOWN) x = start_x left_brace = Brace(Mobject(Point(left), Point(x))) right_brace = Brace(Mobject(Point(x), Point(right)), UP) x_mob = TexMobject("x") x_mob.next_to(left_brace, DOWN) w_minus_x = TexMobject("w-x") w_minus_x.next_to(right_brace, UP) top_line = Line(self.start_point, x) bottom_line = Line(x, self.end_point) top_dist = TexMobject("\\sqrt{h_1^2+x^2}") top_dist.scale(0.5) a = 0.3 n = top_line.get_num_points() point = top_line.points[int(a*n)] top_dist.next_to(Point(point), RIGHT, buff = 0.3) bottom_dist = TexMobject("\\sqrt{h_2^2+(w-x)^2}") bottom_dist.scale(0.5) n = bottom_line.get_num_points() point = bottom_line.points[int((1-a)*n)] bottom_dist.next_to(Point(point), LEFT, buff = 1) end_top_line = Line(self.start_point, end_x) end_bottom_line = Line(end_x, self.end_point) end_brace = Brace(Mobject(Point(left), Point(end_x))) end_x_mob = TexMobject("x").next_to(end_brace, DOWN) axes = Mobject( NumberLine(), NumberLine().rotate(np.pi/2).shift(7*LEFT) ) graph = FunctionGraph( lambda x : 0.4*(x+1)*(x-3)+4, x_min = -2, x_max = 4 ) graph.highlight(YELLOW) Mobject(axes, graph).scale(0.2).to_corner(UP+RIGHT, buff = 1) axes.add(TexMobject("x", size = "\\small").next_to(axes, RIGHT)) axes.add(TextMobject("Travel time", size = "\\small").next_to( axes, UP )) new_graph = graph.copy() midpoint = new_graph.points[new_graph.get_num_points()/2] new_graph.filter_out(lambda p : p[0] < midpoint[0]) new_graph.reverse_points() pairs_for_end_transform = [ (mob, mob.copy()) for mob in top_line, bottom_line, left_brace, x_mob ] self.add(glass, point_a, point_b, A, B) line = Mobject(top_line, bottom_line).ingest_submobjects() self.play(ShowCreation(line)) self.dither() self.play( GrowFromCenter(left_brace), GrowFromCenter(x_mob) ) self.play( GrowFromCenter(right_brace), GrowFromCenter(w_minus_x) ) self.play(ShowCreation(left_line), ShimmerIn(h_1)) self.play(ShowCreation(right_line), GrowFromCenter(h_2)) self.play(ShimmerIn(top_dist)) self.play(GrowFromCenter(bottom_dist)) self.dither(3) self.clear() self.add(glass, point_a, point_b, A, B, top_line, bottom_line, left_brace, x_mob) self.play(ShowCreation(axes)) kwargs = { "run_time" : 4, } self.play(*[ Transform(*pair, **kwargs) for pair in [ (top_line, end_top_line), (bottom_line, end_bottom_line), (left_brace, end_brace), (x_mob, end_x_mob) ] ]+[ShowCreation(graph, **kwargs)]) self.dither() self.show_derivatives(graph) line = self.show_derivatives(new_graph) self.add(line) self.play(*[ Transform(*pair, rate_func = lambda x : 0.3*smooth(x)) for pair in pairs_for_end_transform ]) self.dither() def show_derivatives(self, graph, run_time = 2): step = self.frame_duration/run_time for a in smooth(np.arange(0, 1-step, step)): index = int(a*graph.get_num_points()) p1, p2 = graph.points[index], graph.points[index+1] line = Line(LEFT, RIGHT) line.rotate(angle_of_vector(p2-p1)) line.shift(p1) self.add(line) self.dither(self.frame_duration) self.remove(line) return line class Spring(Line): CONFIG = { "num_loops" : 5, "loop_radius" : 0.3, "color" : GREY } def generate_points(self): ## self.start, self.end length = np.linalg.norm(self.end-self.start) angle = angle_of_vector(self.end-self.start) micro_radius = self.loop_radius/length m = 2*np.pi*(self.num_loops+0.5) def loop(t): return micro_radius*( RIGHT + np.cos(m*t)*LEFT + np.sin(m*t)*UP ) new_epsilon = self.epsilon/(m*micro_radius)/length self.add_points([ t*RIGHT + loop(t) for t in np.arange(0, 1, new_epsilon) ]) self.scale(length/(1+2*micro_radius)) self.rotate(angle) self.shift(self.start) class SpringSetup(ShowMultiplePathsInWater): def construct(self): self.ring_shift_val = 6*RIGHT self.slide_kwargs = { "rate_func" : there_and_back, "run_time" : 5 } self.setup_background() rod = Region( lambda x, y : (abs(x) < 5) & (abs(y) < 0.05), color = GOLD_E ) ring = Arc( angle = 11*np.pi/6, start_angle = -11*np.pi/12, radius = 0.2, color = YELLOW ) ring.shift(-self.ring_shift_val/2) self.generate_springs(ring) self.add_rod_and_ring(rod, ring) self.slide_ring(ring) self.dither() self.add_springs() self.add_force_definitions() self.slide_system(ring) self.show_horizontal_component(ring) self.show_angles(ring) self.show_equation() def setup_background(self): glass = Region(lambda x, y : y < 0, color = BLUE_E) self.generate_start_and_end_points() point_a = Dot(self.start_point) point_b = Dot(self.end_point) A = TextMobject("A").next_to(point_a, UP) B = TextMobject("B").next_to(point_b, DOWN) self.add(glass, point_a, point_b, A, B) def generate_springs(self, ring): self.start_springs, self.end_springs = [ Mobject( Spring(self.start_point, r.get_top()), Spring(self.end_point, r.get_bottom()) ) for r in ring, ring.copy().shift(self.ring_shift_val) ] def add_rod_and_ring(self, rod, ring): rod_word = TextMobject("Rod") rod_word.next_to(Point(), UP) ring_word = TextMobject("Ring") ring_word.next_to(ring, UP) self.dither() self.add(rod) self.play(ShimmerIn(rod_word)) self.dither() self.remove(rod_word) self.play(ShowCreation(ring)) self.play(ShimmerIn(ring_word)) self.dither() self.remove(ring_word) def slide_ring(self, ring): self.play(ApplyMethod( ring.shift, self.ring_shift_val, **self.slide_kwargs )) def add_springs(self): colors = iter([BLACK, BLUE_E]) for spring in self.start_springs.split(): circle = Circle(color = colors.next()) circle.reverse_points() circle.scale(spring.loop_radius) circle.shift(spring.points[0]) self.play(Transform(circle, spring)) self.remove(circle) self.add(spring) self.dither() def add_force_definitions(self): top_force = TexMobject("F_1 = \\dfrac{1}{v_{\\text{air}}}") bottom_force = TexMobject("F_2 = \\dfrac{1}{v_{\\text{water}}}") top_spring, bottom_spring = self.start_springs.split() top_force.next_to(top_spring) bottom_force.next_to(bottom_spring, DOWN, buff = -0.5) words = TextMobject(""" The force in a real spring is proportional to that spring's length """) words.to_corner(UP+RIGHT) for force in top_force, bottom_force: self.play(GrowFromCenter(force)) self.dither() self.play(ShimmerIn(words)) self.dither(3) self.remove(top_force, bottom_force, words) def slide_system(self, ring): equilibrium_slide_kwargs = dict(self.slide_kwargs) def jiggle_to_equilibrium(t): return 0.7*(1+((1-t)**2)*(-np.cos(10*np.pi*t))) equilibrium_slide_kwargs = { "rate_func" : jiggle_to_equilibrium, "run_time" : 3 } start = Mobject(ring, self.start_springs) end = Mobject( ring.copy().shift(self.ring_shift_val), self.end_springs ) for kwargs in self.slide_kwargs, equilibrium_slide_kwargs: self.play(Transform(start, end, **kwargs)) self.dither() def show_horizontal_component(self, ring): v_right = Vector(ring.get_top(), RIGHT) v_left = Vector(ring.get_bottom(), LEFT) self.play(*map(ShowCreation, [v_right, v_left])) self.dither() self.remove(v_right, v_left) def show_angles(self, ring): ring_center = ring.get_center() lines, arcs, thetas = [], [], [] counter = it.count(1) for point in self.start_point, self.end_point: line = Line(point, ring_center, color = GREY) angle = np.pi/2-np.abs(np.arctan(line.get_slope())) arc = Arc(angle, radius = 0.5).rotate(np.pi/2) if point is self.end_point: arc.rotate(np.pi) theta = TexMobject("\\theta_%d"%counter.next()) theta.scale(0.5) theta.shift(2*arc.get_center()) arc.shift(ring_center) theta.shift(ring_center) lines.append(line) arcs.append(arc) thetas.append(theta) vert_line = Line(2*UP, 2*DOWN) vert_line.shift(ring_center) top_spring, bottom_spring = self.start_springs.split() self.play( Transform(ring, Point(ring_center)), Transform(top_spring, lines[0]), Transform(bottom_spring, lines[1]) ) self.play(ShowCreation(vert_line)) anims = [] for arc, theta in zip(arcs, thetas): anims += [ ShowCreation(arc), GrowFromCenter(theta) ] self.play(*anims) self.dither() def show_equation(self): equation = TexMobject([ "\\left(\\dfrac{1}{\\phantom{v_air}}\\right)", "\\sin(\\theta_1)", "=", "\\left(\\dfrac{1}{\\phantom{v_water}}\\right)", "\\sin(\\theta_2)" ]) equation.to_corner(UP+RIGHT) frac1, sin1, equals, frac2, sin2 = equation.split() v_air, v_water = [ TexMobject("v_{\\text{%s}}"%s, size = "\\Large") for s in "air", "water" ] v_air.next_to(Point(frac1.get_center()), DOWN) v_water.next_to(Point(frac2.get_center()), DOWN) frac1.add(v_air) frac2.add(v_water) f1, f2 = [ TexMobject("F_%d"%d, size = "\\Large") for d in 1, 2 ] f1.next_to(sin1, LEFT) f2.next_to(equals, RIGHT) sin2_start = sin2.copy().next_to(f2, RIGHT) bar1 = TexMobject("\\dfrac{\\qquad}{\\qquad}") bar2 = bar1.copy() bar1.next_to(sin1, DOWN) bar2.next_to(sin2, DOWN) v_air_copy = v_air.copy().next_to(bar1, DOWN) v_water_copy = v_water.copy().next_to(bar2, DOWN) bars = Mobject(bar1, bar2) new_eq = equals.copy().center().shift(bars.get_center()) snells = TextMobject("Snell's Law") snells.highlight(YELLOW) snells.shift(new_eq.get_center()[0]*RIGHT) snells.shift(UP) anims = [] for mob in f1, sin1, equals, f2, sin2_start: anims.append(ShimmerIn(mob)) self.play(*anims) self.dither() for f, frac in (f1, frac1), (f2, frac2): target = frac.copy().ingest_submobjects() also = [] if f is f2: also.append(Transform(sin2_start, sin2)) sin2 = sin2_start self.play(Transform(f, target), *also) self.remove(f) self.add(frac) self.dither() self.play( FadeOut(frac1), FadeOut(frac2), Transform(v_air, v_air_copy), Transform(v_water, v_water_copy), ShowCreation(bars), Transform(equals, new_eq) ) self.dither() frac1 = Mobject(sin1, bar1, v_air) frac2 = Mobject(sin2, bar2, v_water) for frac, vect in (frac1, LEFT), (frac2, RIGHT): self.play(ApplyMethod( frac.next_to, equals, vect )) self.dither() self.play(ShimmerIn(snells)) self.dither() class WhatGovernsTheSpeedOfLight(PhotonScene, PathSlidingScene): def construct(self): randy = Randolph() randy.scale(RANDY_SCALE_FACTOR) randy.shift(-randy.get_bottom()) self.add_cycloid_end_points() self.add(self.cycloid) self.slide(randy, self.cycloid) self.play(self.photon_run_along_path(self.cycloid)) self.dither() class WhichPathWouldLightTake(PhotonScene, TryManyPaths): def construct(self): words = TextMobject( ["Which path ", "would \\emph{light} take", "?"] ) words.split()[1].highlight(YELLOW) words.to_corner(UP+RIGHT) self.add_cycloid_end_points() anims = [ self.photon_run_along_path( path, rate_func = smooth ) for path in self.get_paths() ] self.play(anims[0], ShimmerIn(words)) for anim in anims[1:]: self.play(anim) class StateSnellsLaw(PhotonScene): def construct(self): point_a = 3*LEFT+3*UP point_b = 1.5*RIGHT+3*DOWN midpoint = ORIGIN lines, arcs, thetas = [], [], [] counter = it.count(1) for point in point_a, point_b: line = Line(point, midpoint, color = RED_D) angle = np.pi/2-np.abs(np.arctan(line.get_slope())) arc = Arc(angle, radius = 0.5).rotate(np.pi/2) if point is point_b: arc.rotate(np.pi) line.reverse_points() theta = TexMobject("\\theta_%d"%counter.next()) theta.scale(0.5) theta.shift(2*arc.get_center()) arc.shift(midpoint) theta.shift(midpoint) lines.append(line) arcs.append(arc) thetas.append(theta) vert_line = Line(2*UP, 2*DOWN) vert_line.shift(midpoint) path = Mobject(*lines).ingest_submobjects() glass = Region(lambda x, y : y < 0, color = BLUE_E) self.add(glass) equation = TexMobject([ "\\dfrac{\\sin(\\theta_1)}{v_{\\text{air}}}", "=", "\\dfrac{\\sin(\\theta_2)}{v_{\\text{water}}}", ]) equation.to_corner(UP+RIGHT) exp1, equals, exp2 = equation.split() snells_law = TextMobject("Snell's Law:") snells_law.highlight(YELLOW) snells_law.to_edge(UP) self.play(ShimmerIn(snells_law)) self.dither() self.play(ShowCreation(path)) self.play(self.photon_run_along_path(path)) self.dither() self.play(ShowCreation(vert_line)) self.play(*map(ShowCreation, arcs)) self.play(*map(GrowFromCenter, thetas)) self.dither() self.play(ShimmerIn(exp1)) self.dither() self.play(*map(ShimmerIn, [equals, exp2])) self.dither()