from manimlib.imports import * ########## #force_skipping #revert_to_original_skipping_status ########## class Slider(NumberLine): CONFIG = { "color" : WHITE, "x_min" : -1, "x_max" : 1, "unit_size" : 2, "center_value" : 0, "number_scale_val" : 0.75, "label_scale_val" : 1, "numbers_with_elongated_ticks" : [], "line_to_number_vect" : LEFT, "line_to_number_buff" : MED_LARGE_BUFF, "dial_radius" : 0.1, "dial_color" : YELLOW, "include_real_estate_ticks" : True, } def __init__(self, **kwargs): NumberLine.__init__(self, **kwargs) self.rotate(np.pi/2) self.init_dial() if self.include_real_estate_ticks: self.add_real_estate_ticks() def init_dial(self): dial = Dot( radius = self.dial_radius, color = self.dial_color, ) dial.move_to(self.number_to_point(self.center_value)) re_dial = dial.copy() re_dial.set_fill(opacity = 0) self.add(dial, re_dial) self.dial = dial self.re_dial = re_dial self.last_sign = -1 def add_label(self, tex): label = TexMobject(tex) label.scale(self.label_scale_val) label.move_to(self.get_top()) label.shift(MED_LARGE_BUFF*UP) self.add(label) self.label = label def add_real_estate_ticks( self, re_per_tick = 0.05, colors = [BLUE, RED], max_real_estate = 1, ): self.real_estate_ticks = VGroup(*[ self.get_tick(self.center_value + u*np.sqrt(x + re_per_tick)) for x in np.arange(0, max_real_estate, re_per_tick) for u in [-1, 1] ]) self.real_estate_ticks.set_stroke(width = 3) self.real_estate_ticks.set_color_by_gradient(*colors) self.add(self.real_estate_ticks) self.add(self.dial) return self.real_estate_ticks def set_value(self, x): re = (x - self.center_value)**2 for dial, val in (self.dial, x), (self.re_dial, re): dial.move_to(self.number_to_point(val)) return self def set_center_value(self, x): self.center_value = x return self def change_real_estate(self, d_re): left_over = 0 curr_re = self.get_real_estate() if d_re < -curr_re: left_over = d_re + curr_re d_re = -curr_re self.set_real_estate(curr_re + d_re) return left_over def set_real_estate(self, target_re): if target_re < 0: raise Exception("Cannot set real estate below 0") self.re_dial.move_to(self.number_to_point(target_re)) self.update_dial_by_re_dial() return self def get_dial_supplement_animation(self): return UpdateFromFunc(self.dial, self.update_dial_by_re_dial) def update_dial_by_re_dial(self, dial = None): dial = dial or self.dial re = self.get_real_estate() sign = np.sign(self.get_value() - self.center_value) if sign == 0: sign = -self.last_sign self.last_sign *= -1 dial.move_to(self.number_to_point( self.center_value + sign*np.sqrt(abs(re)) )) return dial def get_value(self): return self.point_to_number(self.dial.get_center()) def get_real_estate(self): return self.point_to_number(self.re_dial.get_center()) def copy(self): return self.deepcopy() class SliderScene(Scene): CONFIG = { "n_sliders" : 4, "slider_spacing" : MED_LARGE_BUFF, "slider_config" : {}, "center_point" : None, "total_real_estate" : 1, "ambiently_change_sliders" : False, "ambient_velocity_magnitude" : 1.0, "ambient_acceleration_magnitude" : 1.0, "ambient_jerk_magnitude" : 1.0/2, } def setup(self): if self.center_point is None: self.center_point = np.zeros(self.n_sliders) sliders = VGroup(*[ Slider(center_value = cv, **self.slider_config) for cv in self.center_point ]) sliders.arrange(RIGHT, buff = self.slider_spacing) sliders[0].add_numbers() sliders[0].set_value( self.center_point[0] + np.sqrt(self.total_real_estate) ) self.sliders = sliders self.add_labels_to_sliders() self.add(sliders) def add_labels_to_sliders(self): if len(self.sliders) <= 4: for slider, char in zip(self.sliders, "xyzw"): slider.add_label(char) for slider in self.sliders[1:]: slider.label.align_to(self.sliders[0].label, UP) else: for i, slider in enumerate(self.sliders): slider.add_label("x_{%d}"%(i+1)) return self def reset_dials(self, values, run_time = 1, **kwargs): target_vector = self.get_target_vect_from_subset_of_values(values, **kwargs) radius = np.sqrt(self.total_real_estate) def update_sliders(sliders): curr_vect = self.get_vector() curr_vect -= self.center_point curr_vect *= radius/get_norm(curr_vect) curr_vect += self.center_point self.set_to_vector(curr_vect) return sliders self.play(*[ ApplyMethod(slider.set_value, value) for value, slider in zip(target_vector, self.sliders) ] + [ UpdateFromFunc(self.sliders, update_sliders) ], run_time = run_time) def get_target_vect_from_subset_of_values(self, values, fixed_indices = None): if fixed_indices is None: fixed_indices = [] curr_vector = self.get_vector() target_vector = np.array(self.center_point, dtype = 'float') unspecified_vector = np.array(self.center_point, dtype = 'float') unspecified_indices = [] for i in range(len(curr_vector)): if i < len(values) and values[i] is not None: target_vector[i] = values[i] else: unspecified_indices.append(i) unspecified_vector[i] = curr_vector[i] used_re = get_norm(target_vector - self.center_point)**2 left_over_re = self.total_real_estate - used_re if left_over_re < -0.001: raise Exception("Overspecified reset") uv_norm = get_norm(unspecified_vector - self.center_point) if uv_norm == 0 and left_over_re > 0: unspecified_vector[unspecified_indices] = 1 uv_norm = get_norm(unspecified_vector - self.center_point) if uv_norm > 0: unspecified_vector -= self.center_point unspecified_vector *= np.sqrt(left_over_re)/uv_norm unspecified_vector += self.center_point return target_vector + unspecified_vector - self.center_point def set_to_vector(self, vect): assert len(vect) == len(self.sliders) for slider, value in zip(self.sliders, vect): slider.set_value(value) def get_vector(self): return np.array([slider.get_value() for slider in self.sliders]) def get_center_point(self): return np.array([slider.center_value for slider in self.sliders]) def set_center_point(self, new_center_point): self.center_point = np.array(new_center_point) for x, slider in zip(new_center_point, self.sliders): slider.set_center_value(x) return self def get_current_total_real_estate(self): return sum([ slider.get_real_estate() for slider in self.sliders ]) def get_all_dial_supplement_animations(self): return [ slider.get_dial_supplement_animation() for slider in self.sliders ] def initialize_ambiant_slider_movement(self): self.ambiently_change_sliders = True self.ambient_change_end_time = np.inf self.ambient_change_time = 0 self.ambient_velocity, self.ambient_acceleration, self.ambient_jerk = [ self.get_random_vector(magnitude) for magnitude in [ self.ambient_velocity_magnitude, self.ambient_acceleration_magnitude, self.ambient_jerk_magnitude, ] ] ##Ensure counterclockwise rotations in 2D if len(self.ambient_velocity) == 2: cross = np.cross(self.get_vector(), self.ambient_velocity) if cross < 0: self.ambient_velocity *= -1 self.add_foreground_mobjects(self.sliders) def wind_down_ambient_movement(self, time = 1, wait = True): self.ambient_change_end_time = self.ambient_change_time + time if wait: self.wait(time) if self.skip_animations: self.ambient_change_time += time def ambient_slider_movement_update(self): #Set velocity_magnitude based on start up or wind down velocity_magnitude = float(self.ambient_velocity_magnitude) if self.ambient_change_time <= 1: velocity_magnitude *= smooth(self.ambient_change_time) time_until_end = self.ambient_change_end_time - self.ambient_change_time if time_until_end <= 1: velocity_magnitude *= smooth(time_until_end) if time_until_end < 0: self.ambiently_change_sliders = False return center_point = self.get_center_point() target_vector = self.get_vector() - center_point if get_norm(target_vector) == 0: return vectors_and_magnitudes = [ (self.ambient_acceleration, self.ambient_acceleration_magnitude), (self.ambient_velocity, velocity_magnitude), (target_vector, np.sqrt(self.total_real_estate)), ] jerk = self.get_random_vector(self.ambient_jerk_magnitude) deriv = jerk for vect, mag in vectors_and_magnitudes: vect += self.frame_duration*deriv if vect is self.ambient_velocity: unit_r_vect = target_vector / get_norm(target_vector) vect -= np.dot(vect, unit_r_vect)*unit_r_vect vect *= mag/get_norm(vect) deriv = vect self.set_to_vector(target_vector + center_point) self.ambient_change_time += self.frame_duration def get_random_vector(self, magnitude): result = 2*np.random.random(len(self.sliders)) - 1 result *= magnitude / get_norm(result) return result def update_frame(self, *args, **kwargs): if self.ambiently_change_sliders: self.ambient_slider_movement_update() Scene.update_frame(self, *args, **kwargs) def wait(self, time = 1): if self.ambiently_change_sliders: self.play(Animation(self.sliders, run_time = time)) else: Scene.wait(self,time) ########## class MathIsATease(Scene): def construct(self): randy = Randolph() lashes = VGroup() for eye in randy.eyes: for angle in np.linspace(-np.pi/3, np.pi/3, 12): lash = Line(ORIGIN, RIGHT) lash.set_stroke(DARK_GREY, 2) lash.set_width(0.27) lash.next_to(ORIGIN, RIGHT, buff = 0) lash.rotate(angle + np.pi/2) lash.shift(eye.get_center()) lashes.add(lash) lashes.do_in_place(lashes.stretch, 0.8, 1) lashes.shift(0.04*DOWN) fan = SVGMobject( file_name = "fan", fill_opacity = 1, fill_color = YELLOW, stroke_width = 2, stroke_color = YELLOW, height = 0.7, ) VGroup(*fan[-12:]).set_fill(YELLOW_E) fan.rotate(-np.pi/4) fan.move_to(randy) fan.shift(0.85*UP+0.25*LEFT) self.add(randy) self.play( ShowCreation(lashes, lag_ratio = 0), randy.change, "tease", randy.look, OUT, ) self.add_foreground_mobjects(fan) eye_bottom_y = randy.eyes.get_bottom()[1] self.play( ApplyMethod( lashes.apply_function, lambda p : [p[0], eye_bottom_y, p[2]], rate_func = Blink.CONFIG["rate_func"], ), Blink(randy), DrawBorderThenFill(fan), ) self.play( ApplyMethod( lashes.apply_function, lambda p : [p[0], eye_bottom_y, p[2]], rate_func = Blink.CONFIG["rate_func"], ), Blink(randy), ) self.wait() class TODODeterminants(TODOStub): CONFIG = { "message" : "Determinants clip" } class CircleToPairsOfPoints(Scene): def construct(self): plane = NumberPlane(written_coordinate_height = 0.3) plane.scale(2) plane.add_coordinates(y_vals = [-1, 1]) background_plane = plane.copy() background_plane.set_color(GREY) background_plane.fade() circle = Circle(radius = 2, color = YELLOW) x, y = [np.sqrt(2)/2]*2 dot = Dot(2*x*RIGHT + 2*y*UP, color = LIGHT_GREY) equation = TexMobject("x", "^2", "+", "y", "^2", "=", "1") equation.set_color_by_tex("x", GREEN) equation.set_color_by_tex("y", RED) equation.to_corner(UP+LEFT) equation.add_background_rectangle() coord_pair = TexMobject("(", "-%.02f"%x, ",", "-%.02f"%y, ")") fixed_numbers = coord_pair.get_parts_by_tex("-") fixed_numbers.set_fill(opacity = 0) coord_pair.add_background_rectangle() coord_pair.next_to(dot, UP+RIGHT, SMALL_BUFF) numbers = VGroup(*[ DecimalNumber(val).replace(num, dim_to_match = 1) for val, num in zip([x, y], fixed_numbers) ]) numbers[0].set_color(GREEN) numbers[1].set_color(RED) def get_update_func(i): return lambda t : dot.get_center()[i]/2.0 self.add(background_plane, plane) self.play(ShowCreation(circle)) self.play( FadeIn(coord_pair), Write(numbers, run_time = 1), ShowCreation(dot), ) self.play( Write(equation), *[ Transform( number.copy(), equation.get_parts_by_tex(tex), remover = True ) for tex, number in zip("xy", numbers) ] ) self.play(FocusOn(dot, run_time = 1)) self.play( Rotating( dot, run_time = 7, in_place = False, rate_func = smooth, ), MaintainPositionRelativeTo(coord_pair, dot), *[ ChangingDecimal( num, get_update_func(i), tracked_mobject = fixed_num ) for num, i, fixed_num in zip( numbers, (0, 1), fixed_numbers ) ] ) self.wait() ######### Rotation equations ########## rot_equation = TexMobject( "\\Rightarrow" "\\big(\\cos(\\theta)x - \\sin(\\theta)y\\big)^2 + ", "\\big(\\sin(\\theta)x + \\cos(\\theta)y\\big)^2 = 1", ) rot_equation.scale(0.9) rot_equation.next_to(equation, RIGHT) rot_equation.add_background_rectangle() words = TextMobject("Rotational \\\\ symmetry") words.next_to(ORIGIN, UP) words.to_edge(RIGHT) words.add_background_rectangle() arrow = Arrow( words.get_left(), rot_equation.get_bottom(), path_arc = -np.pi/6 ) randy = Randolph(color = GREY_BROWN) randy.to_corner(DOWN+LEFT) self.play( Write(rot_equation, run_time = 2), FadeOut(coord_pair), FadeOut(numbers), FadeOut(dot), FadeIn(randy) ) self.play(randy.change, "confused", rot_equation) self.play(Blink(randy)) self.play( Write(words, run_time = 1), ShowCreation(arrow), randy.look_at, words ) plane.remove(*plane.coordinate_labels) self.play( Rotate( plane, np.pi/3, run_time = 4, rate_func = there_and_back ), Animation(equation), Animation(rot_equation), Animation(words), Animation(arrow), Animation(circle), randy.change, "hooray" ) self.wait() class GreatSourceOfMaterial(TeacherStudentsScene): def construct(self): self.teacher_says( "It's a great source \\\\ of material.", target_mode = "hooray" ) self.change_student_modes(*["happy"]*3) self.wait(3) class CirclesSpheresSumsSquares(ExternallyAnimatedScene): pass class BackAndForth(Scene): def construct(self): analytic = TextMobject("Analytic") analytic.shift(FRAME_X_RADIUS*LEFT/2) analytic.to_edge(UP, buff = MED_SMALL_BUFF) geometric = TextMobject("Geometric") geometric.shift(FRAME_X_RADIUS*RIGHT/2) geometric.to_edge(UP, buff = MED_SMALL_BUFF) h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS) h_line.to_edge(UP, LARGE_BUFF) v_line = Line(UP, DOWN).scale(FRAME_Y_RADIUS) self.add(analytic, geometric, h_line, v_line) pair = TexMobject("(", "x", ",", "y", ")") pair.shift(FRAME_X_RADIUS*LEFT/2 + FRAME_Y_RADIUS*UP/3) triplet = TexMobject("(", "x", ",", "y", ",", "z", ")") triplet.shift(FRAME_X_RADIUS*LEFT/2 + FRAME_Y_RADIUS*DOWN/2) for mob in pair, triplet: arrow = DoubleArrow(LEFT, RIGHT) arrow.move_to(mob) arrow.shift(2*RIGHT) mob.arrow = arrow circle_eq = TexMobject("x", "^2", "+", "y", "^2", "=", "1") circle_eq.move_to(pair) sphere_eq = TexMobject("x", "^2", "+", "y", "^2", "+", "z", "^2", "=", "1") sphere_eq.move_to(triplet) plane = NumberPlane(x_unit_size = 2, y_unit_size = 2) circle = Circle(radius = 2, color = YELLOW) plane_group = VGroup(plane, circle) plane_group.scale(0.4) plane_group.next_to(h_line, DOWN, SMALL_BUFF) plane_group.shift(FRAME_X_RADIUS*RIGHT/2) self.play(Write(pair)) # self.play(ShowCreation(pair.arrow)) self.play(ShowCreation(plane, run_time = 3)) self.play(Write(triplet)) # self.play(ShowCreation(triplet.arrow)) self.wait(3) for tup, eq, to_draw in (pair, circle_eq, circle), (triplet, sphere_eq, VMobject()): for mob in tup, eq: mob.xyz = VGroup(*[sm for sm in map(mob.get_part_by_tex, "xyz") if sm is not None]) self.play( ReplacementTransform(tup.xyz, eq.xyz), FadeOut(VGroup(*[sm for sm in tup if sm not in tup.xyz])), ) self.play( Write(VGroup(*[sm for sm in eq if sm not in eq.xyz])), ShowCreation(to_draw) ) self.wait(3) class SphereForming(ExternallyAnimatedScene): pass class PreviousVideos(Scene): def construct(self): titles = VGroup(*list(map(TextMobject, [ "Pi hiding in prime regularities", "Visualizing all possible pythagorean triples", "Borsuk-Ulam theorem", ]))) titles.to_edge(UP, buff = MED_SMALL_BUFF) screen = ScreenRectangle(height = 6) screen.next_to(titles, DOWN) title = titles[0] self.add(title, screen) self.wait(2) for new_title in titles[1:]: self.play(Transform(title, new_title)) self.wait(2) class TODOTease(TODOStub): CONFIG = { "message" : "Tease" } class AskAboutLongerLists(TeacherStudentsScene): def construct(self): question = TextMobject( "What about \\\\", "$(x_1, x_2, x_3, x_4)?$" ) tup = question[1] alt_tups = list(map(TextMobject, [ "$(x_1, x_2, x_3, x_4, x_5)?$", "$(x_1, x_2, \\dots, x_{99}, x_{100})?$" ])) self.student_says(question, run_time = 1) self.wait() for alt_tup in alt_tups: alt_tup.move_to(tup) self.play(Transform(tup, alt_tup)) self.wait() self.wait() self.play( RemovePiCreatureBubble(self.students[1]), self.teacher.change, "raise_right_hand" ) self.change_student_modes( *["confused"]*3, look_at_arg = self.teacher.get_top() + 2*UP ) self.play(self.teacher.look, UP) self.wait(5) self.student_says( "I...don't see it.", target_mode = "maybe", student_index = 0 ) self.wait(3) class FourDCubeRotation(ExternallyAnimatedScene): pass class HypersphereRotation(ExternallyAnimatedScene): pass class FourDSurfaceRotating(ExternallyAnimatedScene): pass class Professionals(PiCreatureScene): def construct(self): self.introduce_characters() self.add_equation() self.analogies() def introduce_characters(self): titles = VGroup(*list(map(TextMobject, [ "Mathematician", "Computer scientist", "Physicist", ]))) self.remove(*self.pi_creatures) for title, pi in zip(titles, self.pi_creatures): title.next_to(pi, DOWN) self.play( Animation(VectorizedPoint(pi.eyes.get_center())), FadeIn(pi), Write(title, run_time = 1), ) self.wait() def add_equation(self): quaternion = TexMobject( "\\frac{1}{2}", "+", "0", "\\textbf{i}", "+", "\\frac{\\sqrt{6}}{4}", "\\textbf{j}", "+", "\\frac{\\sqrt{6}}{4}", "\\textbf{k}", ) quaternion.scale(0.7) quaternion.next_to(self.mathy, UP) quaternion.set_color_by_tex_to_color_map({ "i" : RED, "j" : GREEN, "k" : BLUE, }) array = TexMobject("[a_1, a_2, \\dots, a_{100}]") array.next_to(self.compy, UP) kets = TexMobject( "\\alpha", "|\\!\\uparrow\\rangle + ", "\\beta", "|\\!\\downarrow\\rangle" ) kets.set_color_by_tex_to_color_map({ "\\alpha" : GREEN, "\\beta" : RED, }) kets.next_to(self.physy, UP) terms = VGroup(quaternion, array, kets) for term, pi in zip(terms, self.pi_creatures): self.play( Write(term, run_time = 1), pi.change, "pondering", term ) self.wait(2) self.terms = terms def analogies(self): examples = VGroup() plane = ComplexPlane( x_radius = 2.5, y_radius = 1.5, ) plane.add_coordinates() plane.add(Circle(color = YELLOW)) plane.scale(0.75) examples.add(plane) examples.add(Circle()) examples.arrange(RIGHT, buff = 2) examples.to_edge(UP, buff = LARGE_BUFF) labels = VGroup(*list(map(TextMobject, ["2D", "3D"]))) title = TextMobject("Fly by instruments") title.scale(1.5) title.to_edge(UP) for label, example in zip(labels, examples): label.next_to(example, DOWN) self.play( ShowCreation(example), Write(label, run_time = 1) ) example.add(label) self.wait() self.wait() self.play( FadeOut(examples), self.terms.shift, UP, Write(title, run_time = 2) ) self.play(*[ ApplyMethod( pi.change, mode, self.terms.get_left(), run_time = 2, rate_func = squish_rate_func(smooth, a, a+0.5) ) for pi, mode, a in zip( self.pi_creatures, ["confused", "sassy", "erm"], np.linspace(0, 0.5, len(self.pi_creatures)) ) ]) self.wait() self.play(Animation(self.terms[-1])) self.wait(2) ###### def create_pi_creatures(self): self.mathy = Mathematician() self.physy = PiCreature(color = PINK) self.compy = PiCreature(color = PURPLE) pi_creatures = VGroup(self.mathy, self.compy, self.physy) for pi in pi_creatures: pi.scale(0.7) pi_creatures.arrange(RIGHT, buff = 3) pi_creatures.to_edge(DOWN, buff = LARGE_BUFF) return pi_creatures class OfferAHybrid(SliderScene): CONFIG = { "n_sliders" : 3, } def construct(self): self.remove(self.sliders) titles = self.get_titles() h_line = Line(LEFT, RIGHT).scale(FRAME_X_RADIUS) h_line.next_to(titles, DOWN) v_lines = VGroup(*[ Line(UP, DOWN).scale(FRAME_Y_RADIUS) for x in range(2) ]) v_lines.generate_target() for line, vect in zip(v_lines.target, [LEFT, RIGHT]): line.shift(vect*FRAME_X_RADIUS/3) equation = TexMobject("x^2 + y^2 + z^2 = 1") equation.generate_target() equation.shift(FRAME_X_RADIUS*LEFT/2) equation.target.shift(FRAME_WIDTH*LEFT/3) self.add(titles, h_line, v_lines, equation) self.wait() self.play(*list(map(MoveToTarget, [titles, v_lines, equation]))) self.play(Write(self.sliders, run_time = 1)) self.initialize_ambiant_slider_movement() self.wait(10) self.wind_down_ambient_movement() self.wait() def get_titles(self): titles = VGroup(*list(map(TextMobject, [ "Analytic", "Hybrid", "Geometric" ]))) titles.to_edge(UP) titles[1].set_color(BLUE) titles.generate_target() titles[1].scale_in_place(0.001) titles[0].shift(FRAME_X_RADIUS*LEFT/2) titles.target[0].shift(FRAME_WIDTH*LEFT/3) titles[2].shift(FRAME_X_RADIUS*RIGHT/2) titles.target[2].shift(FRAME_WIDTH*RIGHT/3) return titles class TODOBoxExample(TODOStub): CONFIG = { "message" : "Box Example" } class RotatingSphereWithWanderingPoint(ExternallyAnimatedScene): pass class DismissProjection(PiCreatureScene): CONFIG = { "screen_rect_color" : WHITE, "example_vect" : np.array([0.52, 0.26, 0.53, 0.60]), } def construct(self): self.remove(self.pi_creature) self.show_all_spheres() self.discuss_4d_sphere_definition() self.talk_through_animation() self.transition_to_next_scene() def show_all_spheres(self): equations = VGroup(*list(map(TexMobject, [ "x^2 + y^2 = 1", "x^2 + y^2 + z^2 = 1", "x^2 + y^2 + z^2 + w^2 = 1", ]))) colors = [YELLOW, GREEN, BLUE] for equation, edge, color in zip(equations, [LEFT, ORIGIN, RIGHT], colors): equation.set_color(color) equation.shift(3*UP) equation.to_edge(edge) equations[1].shift(LEFT) spheres = VGroup( self.get_circle(equations[0]), self.get_sphere_screen(equations[1], DOWN), self.get_sphere_screen(equations[2], DOWN), ) for equation, sphere in zip(equations, spheres): self.play( Write(equation), LaggedStartMap(ShowCreation, sphere), ) self.wait() self.equations = equations self.spheres = spheres def get_circle(self, equation): result = VGroup( NumberPlane( x_radius = 2.5, y_radius = 2, ).fade(0.4), Circle(color = YELLOW, radius = 1), ) result.scale(0.7) result.next_to(equation, DOWN) return result def get_sphere_screen(self, equation, vect): square = Rectangle() square.set_width(equation.get_width()) square.stretch_to_fit_height(3) square.next_to(equation, vect) square.set_color(self.screen_rect_color) return square def discuss_4d_sphere_definition(self): sphere = self.spheres[-1] equation = self.equations[-1] sphere_words = TextMobject("``4-dimensional sphere''") sphere_words.next_to(sphere, DOWN+LEFT, buff = LARGE_BUFF) arrow = Arrow( sphere_words.get_right(), sphere.get_bottom(), path_arc = np.pi/3, color = BLUE ) descriptor = TexMobject( "\\text{Just lists of numbers like }", "(%.02f \\,, %.02f \\,, %.02f \\,, %.02f \\,)"%tuple(self.example_vect) ) descriptor[1].set_color(BLUE) descriptor.next_to(sphere_words, DOWN) dot = Dot(descriptor[1].get_top()) dot.set_fill(WHITE, opacity = 0.75) self.play( Write(sphere_words), ShowCreation( arrow, rate_func = squish_rate_func(smooth, 0.5, 1) ), run_time = 3, ) self.wait() self.play(Write(descriptor, run_time = 2)) self.wait() self.play( dot.move_to, equation.get_left(), dot.set_fill, None, 0, path_arc = -np.pi/12 ) self.wait(2) self.sphere_words = sphere_words self.sphere_arrow = arrow self.descriptor = descriptor def talk_through_animation(self): sphere = self.spheres[-1] morty = self.pi_creature alt_dims = VGroup(*list(map(TextMobject, ["5D", "6D", "7D"]))) alt_dims.next_to(morty.eyes, UP, SMALL_BUFF) alt_dim = alt_dims[0] self.play(FadeIn(morty)) self.play(morty.change, "raise_right_hand", sphere) self.wait(3) self.play(morty.change, "confused", sphere) self.wait(3) self.play( morty.change, "erm", alt_dims, FadeIn(alt_dim) ) for new_alt_dim in alt_dims[1:]: self.wait() self.play(Transform(alt_dim, new_alt_dim)) self.wait() self.play(morty.change, "concerned_musician") self.play(FadeOut(alt_dim)) self.wait() self.play(morty.change, "angry", sphere) self.wait(2) def transition_to_next_scene(self): equation = self.equations[-1] self.equations.remove(equation) tup = self.descriptor[1] self.descriptor.remove(tup) equation.generate_target() equation.target.center().to_edge(UP) tup.generate_target() tup.target.next_to(equation.target, DOWN) tup.target.set_color(WHITE) self.play(LaggedStartMap(FadeOut, VGroup(*[ self.equations, self.spheres, self.sphere_words, self.sphere_arrow, self.descriptor, self.pi_creature ]))) self.play(*list(map(MoveToTarget, [equation, tup]))) self.wait() ### def create_pi_creature(self): return Mortimer().scale(0.8).to_corner(DOWN+RIGHT) class RotatingSphere(ExternallyAnimatedScene): pass class Introduce4DSliders(SliderScene): CONFIG = { "slider_config" : { "include_real_estate_ticks" : False, "numbers_with_elongated_ticks" : [-1, 0, 1], "tick_frequency" : 0.25, "tick_size" : 0.05, "dial_color" : YELLOW, }, "slider_spacing" : LARGE_BUFF, } def construct(self): self.match_last_scene() self.introduce_sliders() self.ask_about_constraint() def match_last_scene(self): self.start_vect = DismissProjection.CONFIG["example_vect"] self.remove(self.sliders) equation = TexMobject("x^2 + y^2 + z^2 + w^2 = 1") x, y, z, w = self.start_vect tup = TexMobject( "(", "%.02f \\,"%x, ",", "%.02f \\,"%y, ",", "%.02f \\,"%z, ",", "%.02f \\,"%w, ")" ) equation.center().to_edge(UP) equation.set_color(BLUE) tup.next_to(equation, DOWN) self.sliders.next_to(tup, DOWN) self.sliders.shift(0.8*LEFT) self.add(equation, tup) self.wait() self.equation = equation self.tup = tup def introduce_sliders(self): self.set_to_vector(self.start_vect) numbers = self.tup.get_parts_by_tex(".") self.tup.remove(*numbers) dials = VGroup(*[slider.dial for slider in self.sliders]) dial_copies = dials.copy() dials.set_fill(opacity = 0) self.play(LaggedStartMap(FadeIn, self.sliders)) self.play(*[ Transform( num, dial, run_time = 3, rate_func = squish_rate_func(smooth, a, a+0.5), remover = True ) for num, dial, a in zip( numbers, dial_copies, np.linspace(0, 0.5, len(numbers)) ) ]) dials.set_fill(opacity = 1) self.initialize_ambiant_slider_movement() self.play(FadeOut(self.tup)) self.wait(10) def ask_about_constraint(self): equation = self.equation rect = SurroundingRectangle(equation, color = GREEN) randy = Randolph().scale(0.5) randy.next_to(rect, DOWN+LEFT, LARGE_BUFF) self.play(ShowCreation(rect)) self.play(FadeIn(randy)) self.play(randy.change, "pondering", rect) self.wait() for mob in self.sliders, rect: self.play(randy.look_at, mob) self.play(Blink(randy)) self.wait() self.wait() class TwoDimensionalCase(Introduce4DSliders): CONFIG = { "n_sliders" : 2, } def setup(self): SliderScene.setup(self) self.sliders.shift(RIGHT) for number in self.sliders[0].numbers: value = int(number.get_tex_string()) number.move_to(center_of_mass([ slider.number_to_point(value) for slider in self.sliders ])) plane = NumberPlane( x_radius = 2.5, y_radius = 2.5, ) plane.fade(0.25) plane.axes.set_color(GREY) plane.add_coordinates() plane.to_edge(LEFT) origin = plane.coords_to_point(0, 0) circle = Circle(radius = 1, color = WHITE) circle.move_to(plane.coords_to_point(*self.center_point)) dot = Dot(color = YELLOW) dot.move_to(plane.coords_to_point(1, 0)) equation = TexMobject("x^2 + y^2 = 1") equation.to_corner(UP + RIGHT) self.add(plane, circle, dot, equation) self.add_foreground_mobjects(dot) self.plane = plane self.circle = circle self.dot = dot self.equation = equation def construct(self): self.let_values_wander() self.introduce_real_estate() self.let_values_wander(6) self.comment_on_cheap_vs_expensive_real_estate() self.nudge_x_from_one_example() self.note_circle_steepness() self.add_tick_marks() self.write_distance_squared() def let_values_wander(self, total_time = 5): self.initialize_ambiant_slider_movement() self.wait(total_time - 1) self.wind_down_ambient_movement() def introduce_real_estate(self): x_squared_mob = VGroup(*self.equation[:2]) y_squared_mob = VGroup(*self.equation[3:5]) x_rect = SurroundingRectangle(x_squared_mob) y_rect = SurroundingRectangle(y_squared_mob) rects = VGroup(x_rect, y_rect) decimals = VGroup(*[ DecimalNumber(num**2) for num in self.get_vector() ]) decimals.arrange(RIGHT, buff = LARGE_BUFF) decimals.next_to(rects, DOWN, LARGE_BUFF) real_estate_word = TextMobject("``Real estate''") real_estate_word.next_to(decimals, DOWN, MED_LARGE_BUFF) self.play(FadeIn(real_estate_word)) colors = GREEN, RED arrows = VGroup() for rect, decimal, color in zip(rects, decimals, colors): rect.set_color(color) decimal.set_color(color) arrow = Arrow( rect.get_bottom()+SMALL_BUFF*UP, decimal.get_top(), tip_length = 0.2, ) arrow.set_color(color) arrows.add(arrow) self.play(ShowCreation(rect)) self.play( ShowCreation(arrow), Write(decimal) ) self.wait() sliders = self.sliders def create_update_func(i): return lambda alpha : sliders[i].get_real_estate() self.add_foreground_mobjects(decimals) self.decimals = decimals self.decimal_update_anims = [ ChangingDecimal(decimal, create_update_func(i)) for i, decimal in enumerate(decimals) ] self.real_estate_word = real_estate_word def comment_on_cheap_vs_expensive_real_estate(self): blue_rects = VGroup() red_rects = VGroup() for slider in self.sliders: for x1, x2 in (-0.5, 0.5), (0.75, 1.0), (-1.0, -0.75): p1, p2 = list(map(slider.number_to_point, [x1, x2])) rect = Rectangle( stroke_width = 0, fill_opacity = 0.5, width = 0.25, height = (p2-p1)[1] ) rect.move_to((p1+p2)/2) if np.mean([x1, x2]) == 0: rect.set_color(BLUE) blue_rects.add(rect) else: rect.set_color(RED) red_rects.add(rect) blue_rects.save_state() self.play(DrawBorderThenFill(blue_rects)) self.wait() self.play(ReplacementTransform(blue_rects, red_rects)) self.wait() self.play(FadeOut(red_rects)) blue_rects.restore() self.real_estate_rects = VGroup(blue_rects, red_rects) def nudge_x_from_one_example(self): x_re = self.decimals[0] rect = SurroundingRectangle(x_re) self.reset_dials([1, 0]) self.wait() self.play(ShowCreation(rect)) self.play(FadeOut(rect)) self.play(FocusOn(self.dot)) self.wait() self.reset_dials([0.9, -np.sqrt(0.19)]) x_brace, y_brace = [ Brace( VGroup(slider.dial, Dot(slider.number_to_point(0))), vect ) for slider, vect in zip(self.sliders, [LEFT, RIGHT]) ] x_text = x_brace.get_tex("0.9") y_text = y_brace.get_tex("%.02f"%self.sliders[1].get_value()) self.play( GrowFromCenter(x_brace), Write(x_text) ) self.play(ReplacementTransform( VGroup(x_text.copy()), x_re )) self.wait(2) self.play( GrowFromCenter(y_brace), Write(y_text), ) self.wait(2) self.play(FadeIn(self.real_estate_rects)) self.reset_dials([1, 0], run_time = 1) self.reset_dials([0.9, -np.sqrt(0.19)], run_time = 2) self.play(FadeOut(self.real_estate_rects)) self.play(*list(map(FadeOut, [x_brace, y_brace, x_text, y_text]))) self.wait() def note_circle_steepness(self): line = Line( self.plane.coords_to_point(0.5, 1), self.plane.coords_to_point(1.5, -1), ) rect = Rectangle( stroke_width = 0, fill_color = BLUE, fill_opacity = 0.5, ) rect.replace(line, stretch = True) self.play(DrawBorderThenFill(rect, stroke_color = YELLOW)) for x, u in (1, 1), (0.8, 1), (1, 1), (0.8, -1), (1, 1): self.reset_dials([x, u*np.sqrt(1 - x**2)]) self.play(FadeOut(rect)) def add_tick_marks(self): self.remove_foreground_mobjects(self.sliders) self.add(self.sliders) old_ticks = VGroup() all_ticks = VGroup() for slider in self.sliders: slider.tick_size = 0.1 slider.add_real_estate_ticks() slider.remove(slider.get_tick_marks()) all_ticks.add(*slider.real_estate_ticks) old_ticks.add(*slider.get_tick_marks()[:-3]) self.play( FadeOut(old_ticks), ShowCreation(all_ticks, run_time = 3), Animation(VGroup(*[slider.dial for slider in self.sliders])), ) self.add_foreground_mobjects(self.sliders) self.wait() for x in np.arange(0.95, 0.05, -0.05): self.reset_dials( [np.sqrt(x), np.sqrt(1-x)], run_time = 0.5 ) self.wait(0.5) self.initialize_ambiant_slider_movement() self.wait(10) def write_distance_squared(self): d_squared = TexMobject("(\\text{Distance})^2") d_squared.next_to(self.real_estate_word, DOWN) d_squared.set_color(YELLOW) self.play(Write(d_squared)) self.wait(3) ##### def update_frame(self, *args, **kwargs): if hasattr(self, "dot"): x, y = self.get_vector() self.dot.move_to(self.plane.coords_to_point(x, y)) if hasattr(self, "decimals"): for anim in self.decimal_update_anims: anim.update(0) SliderScene.update_frame(self, *args, **kwargs) class TwoDimensionalCaseIntro(TwoDimensionalCase): def construct(self): self.initialize_ambiant_slider_movement() self.wait(10) class ThreeDCase(TwoDimensionalCase): CONFIG = { "n_sliders" : 3, "slider_config" : { "include_real_estate_ticks" : True, "numbers_with_elongated_ticks" : [], "tick_frequency" : 1, "tick_size" : 0.1, }, } def setup(self): SliderScene.setup(self) self.equation = TexMobject( "x^2", "+", "y^2", "+", "z^2", "=", "1" ) self.equation.to_corner(UP+RIGHT) self.add(self.equation) def construct(self): self.force_skipping() self.add_real_estate_decimals() self.initialize_ambiant_slider_movement() self.point_out_third_slider() self.wait(3) self.hold_x_at(0.5, 12) self.revert_to_original_skipping_status() self.hold_x_at(0.85, 12) return self.hold_x_at(1, 5) def add_real_estate_decimals(self): rects = VGroup(*[ SurroundingRectangle(self.equation.get_part_by_tex(char)) for char in "xyz" ]) decimals = VGroup(*[ DecimalNumber(num**2) for num in self.get_vector() ]) decimals.arrange(RIGHT, buff = LARGE_BUFF) decimals.next_to(rects, DOWN, LARGE_BUFF) colors = [GREEN, RED, BLUE] arrows = VGroup() for rect, decimal, color in zip(rects, decimals, colors): rect.set_color(color) decimal.set_color(color) arrow = Arrow( rect.get_bottom()+SMALL_BUFF*UP, decimal.get_top(), tip_length = 0.2, color = color ) arrows.add(arrow) real_estate_word = TextMobject("``Real estate''") real_estate_word.next_to(decimals, DOWN, MED_LARGE_BUFF) sliders = self.sliders def create_update_func(i): return lambda alpha : sliders[i].get_real_estate() self.add_foreground_mobjects(decimals) self.decimals = decimals self.decimal_update_anims = [ ChangingDecimal(decimal, create_update_func(i)) for i, decimal in enumerate(decimals) ] self.add(rects, arrows, real_estate_word) self.rects = rects self.arrows = arrows self.real_estate_word = real_estate_word def point_out_third_slider(self): rect = SurroundingRectangle(self.sliders[-1]) self.wait(4) self.play(ShowCreation(rect)) self.play(FadeOut(rect)) self.wait(8) def hold_x_at(self, x_val, wait_time): #Save these all_sliders = self.sliders original_total_real_estate = self.total_real_estate self.reset_dials([x_val], run_time = 3) self.sliders = VGroup(*self.sliders[1:]) self.total_real_estate = self.total_real_estate-x_val**2 self.initialize_ambiant_slider_movement() self.wait(wait_time-2) self.wind_down_ambient_movement() self.sliders = all_sliders self.total_real_estate = original_total_real_estate self.initialize_ambiant_slider_movement() #### class ThreeDCaseInsert(ThreeDCase): def construct(self): self.add_real_estate_decimals() self.reset_dials([0.85, np.sqrt(1-0.85**2)], run_time = 0) self.reset_dials([1], run_time = 3) self.wait() class SphereAtRest(ExternallyAnimatedScene): pass class BugOnASurface(TeacherStudentsScene): def construct(self): self.teacher_says("You're a bug \\\\ on a surface") self.wait(3) class SphereWithWanderingDotAtX0point5(ExternallyAnimatedScene): pass class MoveSphereSliceFromPoint5ToPoint85(ExternallyAnimatedScene): pass class SphereWithWanderingDotAtX0point85(ExternallyAnimatedScene): pass class MoveSphereSliceFromPoint85To1(ExternallyAnimatedScene): pass class BugOnTheSurfaceSlidersPart(ThreeDCase): CONFIG = { "run_time" : 30 } def construct(self): self.add_real_estate_decimals() self.reset_dials([0.9], run_time = 0) time_range = np.arange(0, self.run_time, self.frame_duration) for time in ProgressDisplay(time_range): t = 0.3*np.sin(2*np.pi*time/7.0) + 1 u = 0.3*np.sin(4*np.pi*time/7.0) + 1.5 self.set_to_vector([ np.cos(u), np.sin(u)*np.cos(t), np.sin(u)*np.sin(t), ]) self.wait(self.frame_duration) class BugOnTheSurfaceSpherePart(ExternallyAnimatedScene): pass class FourDCase(SliderScene, TeacherStudentsScene): def setup(self): TeacherStudentsScene.setup(self) SliderScene.setup(self) self.sliders.scale(0.9) self.sliders.to_edge(UP) self.sliders.shift(2*RIGHT) self.initialize_ambiant_slider_movement() def construct(self): self.show_initial_exchange() self.fix_one_slider() self.ask_now_what() self.set_aside_sliders() def show_initial_exchange(self): dot = Dot(fill_opacity = 0) dot.to_corner(UP+LEFT, buff = 2) self.play(Animation(dot)) self.wait() self.play( Animation(self.sliders), self.teacher.change, "raise_right_hand", ) self.change_student_modes( *["pondering"]*3, look_at_arg = self.sliders ) self.wait(4) def fix_one_slider(self): x_slider = self.sliders[0] dial = x_slider.dial self.wind_down_ambient_movement(wait = False) self.play(self.teacher.change, "speaking") self.sliders.remove(x_slider) self.total_real_estate = get_norm(self.get_vector())**2 self.initialize_ambiant_slider_movement() arrow = Arrow(LEFT, RIGHT, color = GREEN) arrow.next_to(dial, LEFT) self.play( ShowCreation(arrow), dial.set_color, arrow.get_color() ) self.change_student_modes( "erm", "confused", "hooray", look_at_arg = self.sliders, added_anims = [self.teacher.change, "plain"] ) self.wait(5) self.x_slider = x_slider self.x_arrow = arrow def ask_now_what(self): self.student_says( "Okay...now what?", target_mode = "raise_left_hand", student_index = 0, added_anims = [self.teacher.change, "plain"] ) self.change_student_modes( None, "pondering", "pondering", look_at_arg = self.students[0].bubble, ) self.wait(4) self.play(RemovePiCreatureBubble(self.students[0])) def set_aside_sliders(self): self.sliders.add(self.x_slider) self.total_real_estate = 1 self.initialize_ambiant_slider_movement() self.play( self.sliders.scale, 0.5, self.sliders.to_corner, UP+RIGHT, FadeOut(self.x_arrow) ) self.teacher_says( "Time for some \\\\ high-dimensional \\\\ strangeness!", target_mode = "hooray", ) self.wait(7) ##### def non_blink_wait(self, time = 1): SliderScene.wait(self, time) class TwoDBoxExample(Scene): def setup(self): scale_factor = 1.7 self.plane = NumberPlane() self.plane.scale(scale_factor) self.plane.add_coordinates() self.plane.axes.set_color(GREY) self.add(self.plane) def construct(self): self.add_box() self.label_corner_coordinates() self.add_corner_circles() self.add_center_circle() self.compute_radius() def add_box(self): box = Square(color = RED, stroke_width = 6) line = Line( self.plane.coords_to_point(-1, -1), self.plane.coords_to_point(1, 1), ) box.replace(line, stretch = True) self.play(ShowCreation(box)) self.wait() def label_corner_coordinates(self): corner_dots = VGroup() coords_group = VGroup() for x, y in it.product(*[[1, -1]]*2): point = self.plane.coords_to_point(x, y) dot = Dot(point, color = WHITE) coords = TexMobject("(%d, %d)"%(x, y)) coords.add_background_rectangle() coords.next_to(point, point, SMALL_BUFF) corner_dots.add(dot) coords_group.add(coords) self.play( ShowCreation(dot), Write(coords, run_time = 1) ) self.add_foreground_mobjects(coords_group) self.corner_dots = corner_dots self.coords_group = coords_group def add_corner_circles(self): line = Line( self.plane.coords_to_point(-1, 0), self.plane.coords_to_point(1, 0), ) circle = Circle(color = YELLOW) circle.replace(line, dim_to_match = 0) circles = VGroup(*[ circle.copy().move_to(dot) for dot in self.corner_dots ]) radius = Line(ORIGIN, self.plane.coords_to_point(1, 0)) radius.set_stroke(GREY, 6) radius.rotate(-np.pi/4) c0_center = circles[0].get_center() radius.shift(c0_center) r_equals_1 = TexMobject("r = 1") r_equals_1.add_background_rectangle() r_equals_1.next_to( radius.point_from_proportion(0.75), UP+RIGHT, SMALL_BUFF ) self.play(LaggedStartMap(ShowCreation, circles)) self.play( ShowCreation(radius), Write(r_equals_1) ) for angle in -np.pi/4, -np.pi/2, 3*np.pi/4: self.play(Rotating( radius, about_point = c0_center, radians = angle, run_time = 1, rate_func = smooth, )) self.wait(0.5) self.play(*list(map(FadeOut, [radius, r_equals_1]))) self.wait() self.corner_radius = radius self.corner_circles = circles def add_center_circle(self): r = np.sqrt(2) - 1 radius = Line(ORIGIN, self.plane.coords_to_point(r, 0)) radius.set_stroke(WHITE) circle = Circle(color = GREEN) circle.replace( VGroup(radius, radius.copy().rotate(np.pi)), dim_to_match = 0 ) radius.rotate(np.pi/4) r_equals_q = TexMobject("r", "= ???") r_equals_q[1].add_background_rectangle() r_equals_q.next_to(radius, RIGHT, buff = -SMALL_BUFF) self.play(GrowFromCenter(circle, run_time = 2)) self.play(circle.scale, 1.2, rate_func = wiggle) self.play(ShowCreation(radius)) self.play(Write(r_equals_q)) self.wait(2) self.play(FadeOut(r_equals_q[1])) self.inner_radius = radius self.inner_circle = circle self.inner_r = r_equals_q[0] def compute_radius(self): triangle = Polygon( ORIGIN, self.plane.coords_to_point(1, 0), self.plane.coords_to_point(1, 1), fill_color = BLUE, fill_opacity = 0.5, stroke_width = 6, stroke_color = WHITE, ) bottom_one = TexMobject("1") bottom_one.next_to(triangle.get_bottom(), UP, SMALL_BUFF) bottom_one.shift(MED_SMALL_BUFF*RIGHT) side_one = TexMobject("1") side_one.next_to(triangle, RIGHT) sqrt_1_plus_1 = TexMobject("\\sqrt", "{1^2 + 1^2}") sqrt_2 = TexMobject("\\sqrt", "{2}") for sqrt in sqrt_1_plus_1, sqrt_2: sqrt.add_background_rectangle() sqrt.next_to(ORIGIN, UP, SMALL_BUFF) sqrt.rotate(np.pi/4) sqrt.shift(triangle.get_center()) root_2_value = TexMobject("\\sqrt{2} \\approx 1.414") root_2_value.to_corner(UP+RIGHT) root_2_value.add_background_rectangle() root_2_minus_1_value = TexMobject( "\\sqrt{2} - 1 \\approx 0.414" ) root_2_minus_1_value.next_to(root_2_value, DOWN) root_2_minus_1_value.to_edge(RIGHT) root_2_minus_1_value.add_background_rectangle() corner_radius = self.corner_radius c0_center = self.corner_circles[0].get_center() corner_radius.rotate(-np.pi/2, about_point = c0_center) rhs = TexMobject("=", "\\sqrt", "{2}", "-1") rhs.next_to(self.inner_r, RIGHT, SMALL_BUFF, DOWN) rhs.shift(0.5*SMALL_BUFF*DOWN) sqrt_2_target = VGroup(*rhs[1:3]) rhs.add_background_rectangle() self.play( FadeIn(triangle), Write(VGroup(bottom_one, side_one, sqrt_1_plus_1)) ) self.wait(2) self.play(ReplacementTransform(sqrt_1_plus_1, sqrt_2)) self.play( Write(root_2_value, run_time = 1), *list(map(FadeOut, [bottom_one, side_one])) ) self.wait() self.play(ShowCreation(corner_radius)) self.play(Rotating( corner_radius, about_point = c0_center, run_time = 2, rate_func = smooth )) self.play(FadeOut(triangle), Animation(corner_radius)) self.wait() self.play( Write(rhs), Transform(sqrt_2, sqrt_2_target), ) self.play(Write(root_2_minus_1_value)) self.wait(2) class ThreeDBoxExample(ExternallyAnimatedScene): pass class ThreeDCubeCorners(Scene): def construct(self): coordinates = VGroup(*[ TexMobject("(%d,\\, %d,\\, %d)"%(x, y, z)) for x, y, z in it.product(*3*[[1, -1]]) ]) coordinates.arrange(DOWN, aligned_edge = LEFT) name = TextMobject("Corners: ") name.next_to(coordinates[0], LEFT) group = VGroup(name, coordinates) group.set_height(FRAME_HEIGHT - 1) group.to_edge(LEFT) self.play(Write(name, run_time = 2)) self.play(LaggedStartMap(FadeIn, coordinates, run_time = 3)) self.wait() class ShowDistanceFormula(TeacherStudentsScene): def construct(self): rule = TexMobject( "||(", "x_1", ", ", "x_2", "\\dots, ", "x_n", ")||", "=", "\\sqrt", "{x_1^2", " + ", "x_2^2", " +\\cdots", "x_n^2", "}" ) rule.set_color_by_tex_to_color_map({ "x_1" : GREEN, "x_2" : RED, "x_n" : BLUE, }) for part in rule.get_parts_by_tex("x_"): if len(part) > 2: part[1].set_color(WHITE) rule.next_to(self.teacher, UP, LARGE_BUFF) rule.to_edge(RIGHT) rule.shift(UP) rule.save_state() rule.shift(2*DOWN) rule.set_fill(opacity = 0) self.play( rule.restore, self.teacher.change, "raise_right_hand", ) self.wait(3) self.student_says("Why?", student_index = 0) self.play(self.teacher.change, "thinking") self.wait(3) class GeneralizePythagoreanTheoremBeyondTwoD(ThreeDScene): def construct(self): tex_to_color_map = { "x" : GREEN, "y" : RED, "z" : BLUE, } rect = Rectangle( height = 4, width = 5, fill_color = WHITE, fill_opacity = 0.2, ) diag = Line( rect.get_corner(DOWN+LEFT), rect.get_corner(UP+RIGHT), color = YELLOW ) bottom = Line(rect.get_left(), rect.get_right()) bottom.move_to(rect.get_bottom()) bottom.set_color(tex_to_color_map["x"]) side = Line(rect.get_bottom(), rect.get_top()) side.move_to(rect.get_right()) side.set_color(tex_to_color_map["y"]) x = TexMobject("x") x.next_to(rect.get_bottom(), UP, SMALL_BUFF) y = TexMobject("y") y.next_to(rect.get_right(), LEFT, SMALL_BUFF) hyp = TexMobject("\\sqrt", "{x", "^2 + ", "y", "^2}") hyp.set_color_by_tex_to_color_map(tex_to_color_map) hyp.next_to(ORIGIN, UP) hyp.rotate(diag.get_angle()) hyp.shift(diag.get_center()) group = VGroup(rect, bottom, side, diag, x, y, hyp) self.add(rect) for line, tex in (bottom, x), (side, y), (diag, hyp): self.play( ShowCreation(line), Write(tex, run_time = 1) ) self.wait() self.play( group.rotate, 0.45*np.pi, LEFT, group.shift, 2*DOWN ) corner = diag.get_end() z_line = Line(corner, corner + 3*UP) z_line.set_color(tex_to_color_map["z"]) z = TexMobject("z") z.set_color(tex_to_color_map["z"]) z.next_to(z_line, RIGHT) dot = Dot(z_line.get_end()) three_d_diag = Line(diag.get_start(), z_line.get_end()) three_d_diag.set_color(MAROON_B) self.play( ShowCreation(z_line), ShowCreation(dot), Write(z, run_time = 1) ) self.play(ShowCreation(three_d_diag)) self.wait() full_group = VGroup(group, z_line, z, three_d_diag, dot) self.play(Rotating( full_group, radians = -np.pi/6, axis = UP, run_time = 10, )) self.wait() class ThreeDBoxFormulas(Scene): def construct(self): question = TexMobject( "||(1, 1, 1)||", "=", "???" ) answer = TexMobject( "||(1, 1, 1)||", "&=", "\\sqrt{1^2 + 1^2 + 1^2}\\\\", "&= \\sqrt{3}\\\\", "&\\approx", "1.73", ) for mob in question, answer: mob.to_corner(UP+LEFT) inner_r = TexMobject( "\\text{Inner radius}", "&=", "\\sqrt{3} - 1\\\\", "&\\approx", "0.73" ) inner_r.next_to(answer, DOWN, LARGE_BUFF, LEFT) inner_r.set_color(GREEN_C) VGroup(question, answer).shift(0.55*RIGHT) self.play(Write(question)) self.wait(2) self.play(ReplacementTransform(question, answer)) self.wait(2) self.play(Write(inner_r)) self.wait(2) class AskAboutHigherDimensions(TeacherStudentsScene): def construct(self): self.teacher_says( "What happens for \\\\ higher dimensions?" ) self.change_student_modes(*["pondering"]*3) self.wait(2) self.student_thinks( "$\\sqrt{N} - 1$", target_mode = "happy", student_index = 1 ) self.wait() pi = self.students[1] self.play(pi.change, "confused", pi.bubble) self.wait(3) class TenSliders(SliderScene): CONFIG = { "n_sliders" : 10, "run_time": 30, "slider_spacing" : 0.75, "ambient_acceleration_magnitude" : 2.0, } def construct(self): self.initialize_ambiant_slider_movement() self.wait(self.run_time) self.wind_down_ambient_movement() class TwoDBoxWithSliders(TwoDimensionalCase): CONFIG = { "slider_config" : { "include_real_estate_ticks" : True, "tick_frequency" : 1, "numbers_with_elongated_ticks" : [], "tick_size" : 0.1, "dial_color" : YELLOW, "x_min" : -2, "x_max" : 2, "unit_size" : 1.5, }, "center_point" : [1, -1], } def setup(self): TwoDimensionalCase.setup(self) ##Correct from previous setup self.remove(self.equation) self.sliders.shift(RIGHT) VGroup(*self.get_top_level_mobjects()).shift(RIGHT) x_slider = self.sliders[0] for number in x_slider.numbers: value = int(number.get_tex_string()) number.next_to( x_slider.number_to_point(value), LEFT, MED_SMALL_BUFF ) self.plane.axes.set_color(BLUE) ##Add box material corner_circles = VGroup(*[ self.circle.copy().move_to( self.plane.coords_to_point(*coords) ).set_color(GREY) for coords in ((1, 1), (-1, 1), (-1, -1)) ]) line = Line( self.plane.coords_to_point(-1, -1), self.plane.coords_to_point(1, 1), ) box = Square(color = RED) box.replace(line, stretch = True) self.add(box, corner_circles) self.box = box self.corner_circles = corner_circles def construct(self): self.ask_about_off_center_circle() self.recenter_circle() self.write_x_and_y_real_estate() self.swap_with_top_right_circle() self.show_center_circle() self.describe_tangent_point() self.perterb_point() self.wander_on_inner_circle() self.ask_about_inner_real_estate() def ask_about_off_center_circle(self): question = TextMobject("Off-center circles?") question.next_to(self.plane, UP) self.initialize_ambiant_slider_movement() self.play(Write(question)) self.wait(4) self.wind_down_ambient_movement() self.question = question def recenter_circle(self): original_center_point = self.center_point self.play( self.circle.move_to, self.plane.coords_to_point(0, 0), Animation(self.sliders), *[ ApplyMethod( mob.shift, slider.number_to_point(0)-slider.number_to_point(slider.center_value) ) for slider in self.sliders for mob in [slider.real_estate_ticks, slider.dial] ] ) self.center_point = [0, 0] for x, slider in zip(self.center_point, self.sliders): slider.center_value = x self.initialize_ambiant_slider_movement() self.wait(7) self.wind_down_ambient_movement() self.play( self.circle.move_to, self.plane.coords_to_point(*original_center_point), Animation(self.sliders), *[ ApplyMethod( mob.shift, slider.number_to_point(x)-slider.number_to_point(0) ) for x, slider in zip(original_center_point, self.sliders) for mob in [slider.real_estate_ticks, slider.dial] ] ) self.center_point = original_center_point for x, slider in zip(self.center_point, self.sliders): slider.center_value = x self.initialize_ambiant_slider_movement() self.wait(5) def write_x_and_y_real_estate(self): phrases = VGroup( TextMobject("$x$", "real estate:", "$(x-1)^2$"), TextMobject("$y$", "real estate:", "$(y+1)^2$"), ) phrases.next_to(self.plane, UP) phrases[0].set_color_by_tex("x", GREEN) phrases[1].set_color_by_tex("y", RED) x_brace, y_brace = [ Brace(slider.real_estate_ticks, RIGHT) for slider in self.sliders ] x_brace.set_color(GREEN) y_brace.set_color(RED) self.play(FadeOut(self.question)) self.play( Write(phrases[0]), GrowFromCenter(x_brace) ) self.wait(3) self.play( Transform(*phrases), Transform(x_brace, y_brace) ) self.wait(5) self.wind_down_ambient_movement(wait = False) self.play(*list(map(FadeOut, [x_brace, phrases[0]]))) def swap_with_top_right_circle(self): alt_circle = self.corner_circles[0] slider = self.sliders[1] self.play( self.circle.move_to, alt_circle, alt_circle.move_to, self.circle, Animation(slider), *[ ApplyMethod( mob.shift, slider.number_to_point(1) - slider.number_to_point(-1) ) for mob in (slider.real_estate_ticks, slider.dial) ] ) slider.center_value = 1 self.center_point[1] = 1 self.initialize_ambiant_slider_movement() self.wait(3) def show_center_circle(self): origin = self.plane.coords_to_point(0, 0) radius = get_norm( self.plane.coords_to_point(np.sqrt(2)-1, 0) - origin ) circle = Circle(radius = radius, color = GREEN) circle.move_to(origin) self.play(FocusOn(circle)) self.play(GrowFromCenter(circle, run_time = 2)) self.wait(3) def describe_tangent_point(self): target_vector = np.array([ 1-np.sqrt(2)/2, 1-np.sqrt(2)/2 ]) point = self.plane.coords_to_point(*target_vector) origin = self.plane.coords_to_point(0, 0) h_line = Line(point[1]*UP + origin[0]*RIGHT, point) v_line = Line(point[0]*RIGHT+origin[1]*UP, point) while get_norm(self.get_vector()-target_vector) > 0.5: self.wait() self.wind_down_ambient_movement(0) self.reset_dials(target_vector) self.play(*list(map(ShowCreation, [h_line, v_line]))) self.wait() re_line = DashedLine( self.sliders[0].dial.get_left() + MED_SMALL_BUFF*LEFT, self.sliders[1].dial.get_right() + MED_SMALL_BUFF*RIGHT, ) words = TextMobject("Evenly shared \\\\ real estate") words.scale(0.8) words.next_to(re_line, RIGHT) self.play(ShowCreation(re_line)) self.play(Write(words)) self.wait() self.evenly_shared_words = words self.re_line = re_line def perterb_point(self): #Perturb dials target_vector = np.array([ 1 - np.sqrt(0.7), 1 - np.sqrt(0.3), ]) ghost_dials = VGroup(*[ slider.dial.copy() for slider in self.sliders ]) ghost_dials.set_fill(WHITE, opacity = 0.75) self.add_foreground_mobjects(ghost_dials) self.reset_dials(target_vector) self.wait() #Comment on real estate exchange x_words = TextMobject("Gain expensive \\\\", "real estate") y_words = TextMobject("Give up cheap \\\\", "real estate") VGroup(x_words, y_words).scale(0.8) x_words.next_to(self.re_line, UP+LEFT) x_words.shift(SMALL_BUFF*(DOWN+LEFT)) y_words.next_to(self.re_line, UP+RIGHT) y_words.shift(MED_LARGE_BUFF*UP) x_arrow, y_arrow = [ Arrow( words[1].get_edge_center(vect), self.sliders[i].dial, tip_length = 0.15, ) for i, words, vect in zip( (0, 1), [x_words, y_words], [RIGHT, LEFT] ) ] self.play( Write(x_words, run_time = 2), ShowCreation(x_arrow) ) self.wait() self.play(FadeOut(self.evenly_shared_words)) self.play( Write(y_words, run_time = 2), ShowCreation(y_arrow) ) self.wait(2) #Swap perspective word_starts = VGroup(y_words[0], x_words[0]) crosses = VGroup() new_words = VGroup() for w1, w2 in zip(word_starts, reversed(word_starts)): crosses.add(Cross(w1)) w1_copy = w1.copy() w1_copy.generate_target() w1_copy.target.next_to(w2, UP, SMALL_BUFF) new_words.add(w1_copy) self.play(*[ ApplyMethod( slider.real_estate_ticks.shift, slider.number_to_point(0)-slider.number_to_point(1) ) for slider in self.sliders ]) self.wait() self.play(ShowCreation(crosses)) self.play( LaggedStartMap(MoveToTarget, new_words), Animation(crosses) ) self.wait(3) #Return to original position target_vector = np.array(2*[1-np.sqrt(0.5)]) self.play(LaggedStartMap(FadeOut, VGroup(*[ ghost_dials, x_words, y_words, x_arrow, y_arrow, crosses, new_words, ]))) self.remove_foreground_mobjects(ghost_dials) self.reset_dials(target_vector) self.center_point = np.zeros(2) for x, slider in zip(self.center_point, self.sliders): slider.center_value = x self.set_to_vector(target_vector) self.total_real_estate = self.get_current_total_real_estate() self.wait(2) def wander_on_inner_circle(self): self.initialize_ambiant_slider_movement() self.wait(9) def ask_about_inner_real_estate(self): question = TextMobject("What is \\\\ $x^2 + y^2$?") question.next_to(self.re_line, RIGHT) rhs = TexMobject("<0.5^2 + 0.5^2") rhs.scale(0.8) rhs.next_to(question, DOWN) rhs.to_edge(RIGHT) half_line = Line(*[ slider.number_to_point(0.5) + MED_LARGE_BUFF*vect for slider, vect in zip(self.sliders, [LEFT, RIGHT]) ]) half = TexMobject("0.5") half.scale(self.sliders[0].number_scale_val) half.next_to(half_line, LEFT, SMALL_BUFF) target_vector = np.array(2*[1-np.sqrt(0.5)]) while get_norm(target_vector - self.get_vector()) > 0.5: self.wait() self.wind_down_ambient_movement(0) self.reset_dials(target_vector) self.play(Write(question)) self.wait(3) self.play( ShowCreation(half_line), Write(half) ) self.wait() self.play(Write(rhs)) self.wait(3) class AskWhy(TeacherStudentsScene): def construct(self): self.student_says( "Wait, why?", target_mode = "confused" ) self.wait(3) class MentionComparisonToZeroPointFive(TeacherStudentsScene): def construct(self): self.teacher_says( "Comparing to $0.5$ will \\\\"+\ "be surprisingly useful!", target_mode = "hooray" ) self.change_student_modes(*["happy"]*3) self.wait(3) class ThreeDBoxExampleWithSliders(SliderScene): CONFIG = { "n_sliders" : 3, "slider_config" : { "x_min" : -2, "x_max" : 2, "unit_size" : 1.5, }, "center_point" : np.ones(3), } def setup(self): SliderScene.setup(self) self.sliders.shift(2*RIGHT) def construct(self): self.initialize_ambiant_slider_movement() self.name_corner_sphere() self.point_out_closest_point() self.compare_to_halfway_point() self.reframe_as_inner_sphere_point() self.place_bound_on_inner_real_estate() self.comment_on_inner_sphere_smallness() def name_corner_sphere(self): sphere_name = TextMobject( """Sphere with radius 1\\\\ centered at (1, 1, 1)""" ) sphere_name.to_corner(UP+LEFT) arrow = Arrow( sphere_name, VGroup(*self.sliders[0].numbers[-2:]), color = BLUE ) self.play( LaggedStartMap(FadeIn, sphere_name,), ShowCreation(arrow, rate_func = squish_rate_func(smooth, 0.7, 1)), run_time = 3 ) self.wait(5) self.sphere_name = sphere_name self.arrow = arrow def point_out_closest_point(self): target_x = 1-np.sqrt(1./3) target_vector = np.array(3*[target_x]) re_words = TextMobject( "$x$, $y$ and $z$ each have \\\\", "$\\frac{1}{3}$", "units of real estate" ) re_words.to_corner(UP+LEFT) re_line = DashedLine(*[ self.sliders[i].number_to_point(target_x) + MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (2, RIGHT)] ]) new_arrow = Arrow( re_words.get_corner(DOWN+RIGHT), re_line.get_left(), color = BLUE ) self.wind_down_ambient_movement() self.play(*[ ApplyMethod(slider.set_value, x) for x, slider in zip(target_vector, self.sliders) ]) self.play(ShowCreation(re_line)) self.play( FadeOut(self.sphere_name), Transform(self.arrow, new_arrow), ) self.play(LaggedStartMap(FadeIn, re_words)) self.wait(2) self.re_words = re_words self.re_line = re_line def compare_to_halfway_point(self): half_line = Line(*[ self.sliders[i].number_to_point(0.5)+MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (2, RIGHT)] ]) half_line.set_stroke(MAROON_B, 6) half_label = TexMobject("0.5") half_label.scale(self.sliders[0].number_scale_val) half_label.next_to(half_line, LEFT, MED_SMALL_BUFF) half_label.set_color(half_line.get_color()) curr_vector = self.get_vector() target_vector = 0.5*np.ones(3) ghost_dials = VGroup(*[ slider.dial.copy().set_fill(WHITE, 0.5) for slider in self.sliders ]) cross = Cross(self.re_words.get_parts_by_tex("frac")) new_re = TexMobject("(0.5)^2 = 0.25") new_re.next_to(cross, DOWN, MED_SMALL_BUFF, LEFT) new_re.set_color(MAROON_B) self.play( FadeOut(self.arrow), Write(half_label, run_time = 1), ShowCreation(half_line) ) self.wait() self.add(ghost_dials) self.play(*[ ApplyMethod(slider.set_value, 0.5) for slider in self.sliders ]) self.play(ShowCreation(cross)) self.play(Write(new_re)) self.wait(3) self.play( FadeOut(new_re), FadeOut(cross), *[ ApplyMethod(slider.set_value, x) for x, slider in zip(curr_vector, self.sliders) ] ) def reframe_as_inner_sphere_point(self): s = self.sliders[0] shift_vect = s.number_to_point(0)-s.number_to_point(1) curr_vector = self.get_vector() self.set_center_point(np.zeros(3)) self.set_to_vector(curr_vector) self.total_real_estate = self.get_current_total_real_estate() all_re_ticks = VGroup(*[ slider.real_estate_ticks for slider in self.sliders ]) inner_sphere_words = TextMobject( "Also a point on \\\\", "the inner sphere" ) inner_sphere_words.next_to(self.re_line, RIGHT) question = TextMobject("How much \\\\", "real estate?") question.next_to(self.re_line, RIGHT, MED_LARGE_BUFF) self.play( Animation(self.sliders), FadeOut(self.re_words), LaggedStartMap( ApplyMethod, all_re_ticks, lambda m : (m.shift, shift_vect) ) ) self.initialize_ambiant_slider_movement() self.play(Write(inner_sphere_words)) self.wait(5) self.wind_down_ambient_movement(0) self.play(*[ ApplyMethod(slider.set_value, x) for slider, x in zip(self.sliders, curr_vector) ]) self.play(ReplacementTransform( inner_sphere_words, question )) self.wait(2) self.re_question = question def place_bound_on_inner_real_estate(self): bound = TexMobject( "&< 3(0.5)^2 ", "= 0.75" ) bound.next_to(self.re_question, DOWN) bound.to_edge(RIGHT) self.play(Write(bound)) self.wait(2) def comment_on_inner_sphere_smallness(self): self.initialize_ambiant_slider_movement() self.wait(15) class Rotating3DCornerSphere(ExternallyAnimatedScene): pass class FourDBoxExampleWithSliders(ThreeDBoxExampleWithSliders): CONFIG = { "n_sliders" : 4, "center_point" : np.ones(4), } def construct(self): self.list_corner_coordinates() self.show_16_corner_spheres() self.show_closest_point() self.show_real_estate_at_closest_point() self.reframe_as_inner_sphere_point() self.compute_inner_radius_numerically() self.inner_sphere_touches_box() def list_corner_coordinates(self): title = TextMobject( "$2 \\!\\times\\! 2 \\!\\times\\! 2 \\!\\times\\! 2$ box vertices:" ) title.shift(FRAME_X_RADIUS*LEFT/2) title.to_edge(UP) coordinates = list(it.product(*4*[[1, -1]])) coordinate_mobs = VGroup(*[ TexMobject("(%d, %d, %d, %d)"%tup) for tup in coordinates ]) coordinate_mobs.arrange(DOWN, aligned_edge = LEFT) coordinate_mobs.scale(0.8) left_column = VGroup(*coordinate_mobs[:8]) right_column = VGroup(*coordinate_mobs[8:]) right_column.next_to(left_column, RIGHT) coordinate_mobs.next_to(title, DOWN) self.play(Write(title)) self.play(LaggedStartMap(FadeIn, coordinate_mobs)) self.wait() self.coordinate_mobs = coordinate_mobs self.coordinates = coordinates self.box_vertices_title = title def show_16_corner_spheres(self): sphere_words = VGroup(TextMobject("Sphere centered at")) sphere_words.scale(0.8) sphere_words.next_to(self.sliders, RIGHT) sphere_words.shift(2*UP) self.add(sphere_words) pairs = list(zip(self.coordinate_mobs, self.coordinates)) for coord_mob, coords in pairs[1:] + [pairs[0]]: coord_mob.set_color(GREEN) coord_mob_copy = coord_mob.copy() coord_mob_copy.next_to(sphere_words, DOWN) for slider, x in zip(self.sliders, coords): point = slider.number_to_point(x) slider.real_estate_ticks.move_to(point) slider.dial.move_to(point) self.sliders[0].dial.move_to( self.sliders[0].number_to_point(coords[0]+1) ) self.add(coord_mob_copy) self.wait() self.remove(coord_mob_copy) coord_mob.set_color(WHITE) self.add(coord_mob_copy) sphere_words.add(coord_mob_copy) self.sphere_words = sphere_words self.play( self.sliders.center, sphere_words.shift, LEFT, *list(map(FadeOut, [ self.coordinate_mobs, self.box_vertices_title ])) ) self.initialize_ambiant_slider_movement() self.wait(4) def show_closest_point(self): target_vector = 0.5*np.ones(4) re_line = DashedLine(*[ self.sliders[i].number_to_point(0.5)+MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (-1, RIGHT)] ]) half_label = TexMobject("0.5") half_label.scale(self.sliders[0].number_scale_val) half_label.next_to(re_line, LEFT, MED_SMALL_BUFF) half_label.set_color(MAROON_B) self.wind_down_ambient_movement() self.play(*[ ApplyMethod(slider.set_value, x) for x, slider in zip(target_vector, self.sliders) ]) self.play(ShowCreation(re_line)) self.play(Write(half_label)) self.wait(2) self.re_line = re_line self.half_label = half_label def show_real_estate_at_closest_point(self): words = TextMobject("Total real estate:") value = TexMobject("4(0.5)^2 = 4(0.25) = 1") value.next_to(words, DOWN) re_words = VGroup(words, value) re_words.scale(0.8) re_words.next_to(self.sphere_words, DOWN, MED_LARGE_BUFF) re_rects = VGroup() for slider in self.sliders: rect = Rectangle( width = 2*slider.tick_size, height = 0.5*slider.unit_size, stroke_width = 0, fill_color = MAROON_B, fill_opacity = 0.75, ) rect.move_to(slider.number_to_point(0.75)) re_rects.add(rect) self.play(FadeIn(re_words)) self.play(LaggedStartMap(DrawBorderThenFill, re_rects, run_time = 3)) self.wait(2) self.re_words = re_words self.re_rects = re_rects def reframe_as_inner_sphere_point(self): sphere_words = self.sphere_words sphere_words.generate_target() sphere_words.target.shift(2*DOWN) old_coords = sphere_words.target[1] new_coords = TexMobject("(0, 0, 0, 0)") new_coords.replace(old_coords, dim_to_match = 1) new_coords.set_color(old_coords.get_color()) Transform(old_coords, new_coords).update(1) self.play(Animation(self.sliders), *[ ApplyMethod( s.real_estate_ticks.move_to, s.number_to_point(0), run_time = 2, rate_func = squish_rate_func(smooth, a, a+0.5) ) for s, a in zip(self.sliders, np.linspace(0, 0.5, 4)) ]) self.play( MoveToTarget(sphere_words), self.re_words.next_to, sphere_words.target, UP, MED_LARGE_BUFF, path_arc = np.pi ) self.wait(2) re_shift_vect = 0.5*self.sliders[0].unit_size*DOWN self.play(LaggedStartMap( ApplyMethod, self.re_rects, lambda m : (m.shift, re_shift_vect), path_arc = np.pi )) self.wait() re_words_rect = SurroundingRectangle(self.re_words) self.play(ShowCreation(re_words_rect)) self.wait() self.play(FadeOut(re_words_rect)) self.wait() self.set_center_point(np.zeros(4)) self.initialize_ambiant_slider_movement() self.wait(4) def compute_inner_radius_numerically(self): computation = TexMobject( "R_\\text{Inner}", "&= ||(1, 1, 1, 1)|| - 1 \\\\", # "&= \\sqrt{1^2 + 1^2 + 1^2 + 1^2} - 1 \\\\", "&= \\sqrt{4} - 1 \\\\", "&= 1" ) computation.scale(0.8) computation.to_corner(UP+LEFT) computation.shift(DOWN) brace = Brace(VGroup(*computation[1][1:-2]), UP) brace_text = brace.get_text("Distance to corner") brace_text.scale(0.8, about_point = brace_text.get_bottom()) VGroup(brace, brace_text).set_color(RED) self.play(LaggedStartMap(FadeIn, computation, run_time = 3)) self.play(GrowFromCenter(brace)) self.play(Write(brace_text, run_time = 2)) self.wait(16) computation.add(brace, brace_text) self.computation = computation def inner_sphere_touches_box(self): touching_words = TextMobject( "This point touches\\\\", "the $2 \\!\\times\\! 2 \\!\\times\\! 2 \\!\\times\\! 2$ box!" ) touching_words.to_corner(UP+LEFT) arrow = Arrow(MED_SMALL_BUFF*DOWN, 3*RIGHT+DOWN) arrow.set_color(BLUE) arrow.shift(touching_words.get_bottom()) self.wind_down_ambient_movement(wait = False) self.play(FadeOut(self.computation)) self.reset_dials([1]) self.play(Write(touching_words)) self.play(ShowCreation(arrow)) self.wait(2) class TwoDInnerSphereTouchingBox(TwoDBoxWithSliders, PiCreatureScene): def setup(self): TwoDBoxWithSliders.setup(self) PiCreatureScene.setup(self) self.remove(self.sliders) self.remove(self.dot) self.circle.set_color(GREY) self.randy.next_to(self.plane, RIGHT, LARGE_BUFF, DOWN) def construct(self): little_inner_circle, big_inner_circle = [ Circle( radius = radius*self.plane.x_unit_size, color = GREEN ).move_to(self.plane.coords_to_point(0, 0)) for radius in (np.sqrt(2)-1, 1) ] randy = self.randy tangency_points = VGroup(*[ Dot(self.plane.coords_to_point(x, y)) for x, y in [(1, 0), (0, 1), (-1, 0), (0, -1)] ]) tangency_points.set_fill(YELLOW, 0.5) self.play( ShowCreation(little_inner_circle), randy.change, "pondering", little_inner_circle ) self.wait() self.play( ReplacementTransform( little_inner_circle.copy(), big_inner_circle ), little_inner_circle.fade, randy.change, "confused" ) big_inner_circle.save_state() self.play(big_inner_circle.move_to, self.circle) self.play(big_inner_circle.restore) self.wait() self.play(LaggedStartMap( DrawBorderThenFill, tangency_points, rate_func = double_smooth )) self.play(randy.change, "maybe") self.play(randy.look_at, self.circle) self.wait() self.play(randy.look_at, little_inner_circle) self.wait() #### def create_pi_creature(self): self.randy = Randolph().flip() return self.randy class FiveDBoxExampleWithSliders(FourDBoxExampleWithSliders): CONFIG = { "n_sliders" : 5, "center_point" : np.ones(5), } def setup(self): FourDBoxExampleWithSliders.setup(self) self.sliders.center() def construct(self): self.show_32_corner_spheres() self.show_closest_point() self.show_halfway_point() self.reframe_as_inner_sphere_point() self.compute_radius() self.poke_out_of_box() def show_32_corner_spheres(self): sphere_words = VGroup(TextMobject("Sphere centered at")) sphere_words.next_to(self.sliders, RIGHT, MED_LARGE_BUFF) sphere_words.shift(2.5*UP) self.add(sphere_words) n_sphere_words = TextMobject("32 corner spheres") n_sphere_words.to_edge(LEFT) n_sphere_words.shift(2*UP) self.add(n_sphere_words) for coords in it.product(*5*[[-1, 1]]): s = str(tuple(coords)) s = s.replace("1", "+1") s = s.replace("-+1", "-1") coords_mob = TexMobject(s) coords_mob.set_color(GREEN) coords_mob.next_to(sphere_words, DOWN) for slider, x in zip(self.sliders, coords): for mob in slider.real_estate_ticks, slider.dial: mob.move_to(slider.number_to_point(x)) self.sliders[0].dial.move_to( self.sliders[0].number_to_point(coords[0]+1) ) self.add(coords_mob) self.wait(0.25) self.remove(coords_mob) self.add(coords_mob) sphere_words.add(coords_mob) self.sphere_words = sphere_words self.initialize_ambiant_slider_movement() self.play(FadeOut(n_sphere_words)) self.wait(3) def show_closest_point(self): target_x = 1-np.sqrt(0.2) re_line = DashedLine(*[ self.sliders[i].number_to_point(target_x)+MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (-1, RIGHT)] ]) re_words = TextMobject( "$0.2$", "units of real \\\\ estate each" ) re_words.next_to(self.sphere_words, DOWN, MED_LARGE_BUFF) re_rects = VGroup() for slider in self.sliders: rect = Rectangle( width = 2*slider.tick_size, height = (1-target_x)*slider.unit_size, stroke_width = 0, fill_color = GREEN, fill_opacity = 0.75, ) rect.move_to(slider.number_to_point(1), UP) re_rects.add(rect) self.wind_down_ambient_movement() self.reset_dials(5*[target_x]) self.play( ShowCreation(re_line), Write(re_words, run_time = 2) ) self.play(LaggedStartMap( DrawBorderThenFill, re_rects, rate_func = double_smooth )) self.wait() self.re_rects = re_rects self.re_words = re_words self.re_line = re_line def show_halfway_point(self): half_line = Line(*[ self.sliders[i].number_to_point(0.5)+MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (-1, RIGHT)] ]) half_line.set_color(MAROON_B) half_label = TexMobject("0.5") half_label.scale(self.sliders[0].number_scale_val) half_label.next_to(half_line, LEFT, MED_SMALL_BUFF) half_label.set_color(half_line.get_color()) curr_vector = self.get_vector() ghost_dials = VGroup(*[ slider.dial.copy().set_fill(WHITE, 0.75) for slider in self.sliders ]) point_25 = TexMobject("0.25") point_25.set_color(half_label.get_color()) point_25.move_to(self.re_words[0], RIGHT) self.re_words.save_state() self.play( Write(half_label), ShowCreation(half_line) ) self.wait(2) self.add(ghost_dials) self.play(*[ ApplyMethod(slider.set_value, 0.5) for slider in self.sliders ]) self.play(Transform(self.re_words[0], point_25)) self.wait(2) self.play(*[ ApplyMethod(slider.set_value, x) for x, slider in zip(curr_vector, self.sliders) ]) self.play(self.re_words.restore) def reframe_as_inner_sphere_point(self): s = self.sliders[0] shift_vect = s.number_to_point(0)-s.number_to_point(1) re_ticks = VGroup(*[ slider.real_estate_ticks for slider in self.sliders ]) re_rects = self.re_rects re_rects.generate_target() for rect, slider in zip(re_rects.target, self.sliders): height = slider.unit_size*(1-np.sqrt(0.2)) rect.set_height(height) rect.move_to(slider.number_to_point(0), DOWN) self.sphere_words.generate_target() old_coords = self.sphere_words.target[1] new_coords = TexMobject(str(tuple(5*[0]))) new_coords.replace(old_coords, dim_to_match = 1) new_coords.set_color(old_coords.get_color()) Transform(old_coords, new_coords).update(1) self.re_words.generate_target() new_re = TexMobject("0.31") new_re.set_color(GREEN) old_re = self.re_words.target[0] new_re.move_to(old_re, RIGHT) Transform(old_re, new_re).update(1) self.play( Animation(self.sliders), LaggedStartMap( ApplyMethod, re_ticks, lambda m : (m.shift, shift_vect), path_arc = np.pi ), MoveToTarget(self.sphere_words), ) self.play( MoveToTarget( re_rects, run_time = 2, lag_ratio = 0.5, path_arc = np.pi ), MoveToTarget(self.re_words), ) self.wait(2) self.set_center_point(np.zeros(5)) self.total_real_estate = (np.sqrt(5)-1)**2 self.initialize_ambiant_slider_movement() self.wait(12) def compute_radius(self): computation = TexMobject( "R_{\\text{inner}} &= \\sqrt{5}-1 \\\\", "&\\approx 1.24" ) computation.to_corner(UP+LEFT) self.play(Write(computation, run_time = 2)) self.wait(12) def poke_out_of_box(self): self.wind_down_ambient_movement(0) self.reset_dials([np.sqrt(5)-1]) words = TextMobject("Poking outside \\\\ the box!") words.to_edge(LEFT) words.set_color(RED) arrow = Arrow( words.get_top(), self.sliders[0].dial, path_arc = -np.pi/3, color = words.get_color() ) self.play( ShowCreation(arrow), Write(words) ) self.wait(2) class SkipAheadTo10(TeacherStudentsScene): def construct(self): self.teacher_says( "Let's skip ahead \\\\ to 10 dimensions", target_mode = "hooray" ) self.change_student_modes( "pleading", "confused", "horrified" ) self.wait(3) class TenDBoxExampleWithSliders(FiveDBoxExampleWithSliders): CONFIG = { "n_sliders" : 10, "center_point" : np.ones(10), "ambient_velocity_magnitude" : 2.0, "ambient_acceleration_magnitude" : 3.0, } def setup(self): FourDBoxExampleWithSliders.setup(self) self.sliders.to_edge(RIGHT) def construct(self): self.initial_wandering() self.show_closest_point() self.reframe_as_inner_sphere_point() self.compute_inner_radius_numerically() self.wander_on_inner_sphere() self.poke_outside_outer_box() def initial_wandering(self): self.initialize_ambiant_slider_movement() self.wait(9) def show_closest_point(self): target_x = 1-np.sqrt(1./self.n_sliders) re_line = DashedLine(*[ self.sliders[i].number_to_point(target_x)+MED_SMALL_BUFF*vect for i, vect in [(0, LEFT), (-1, RIGHT)] ]) re_rects = VGroup() for slider in self.sliders: rect = Rectangle( width = 2*slider.tick_size, height = (1-target_x)*slider.unit_size, stroke_width = 0, fill_color = GREEN, fill_opacity = 0.75, ) rect.move_to(slider.number_to_point(1), UP) re_rects.add(rect) self.wind_down_ambient_movement() self.reset_dials(self.n_sliders*[target_x]) self.play(ShowCreation(re_line)) self.play(LaggedStartMap( DrawBorderThenFill, re_rects, rate_func = double_smooth )) self.wait(2) self.re_line = re_line self.re_rects = re_rects def reframe_as_inner_sphere_point(self): s = self.sliders[0] shift_vect = s.number_to_point(0)-s.number_to_point(1) re_ticks = VGroup(*[ slider.real_estate_ticks for slider in self.sliders ]) re_rects = self.re_rects re_rects.generate_target() for rect, slider in zip(re_rects.target, self.sliders): height = slider.unit_size*(1-np.sqrt(1./self.n_sliders)) rect.stretch_to_fit_height(height) rect.move_to(slider.number_to_point(0), DOWN) self.play( Animation(self.sliders), LaggedStartMap( ApplyMethod, re_ticks, lambda m : (m.shift, shift_vect), path_arc = np.pi ), ) self.play( MoveToTarget( re_rects, run_time = 2, lag_ratio = 0.5, path_arc = np.pi ), ) self.wait(2) self.set_center_point(np.zeros(self.n_sliders)) self.total_real_estate = (np.sqrt(self.n_sliders)-1)**2 self.initialize_ambiant_slider_movement() self.wait(5) def compute_inner_radius_numerically(self): computation = TexMobject( "R_{\\text{inner}} &= \\sqrt{10}-1 \\\\", "&\\approx 2.16" ) computation.to_corner(UP+LEFT) self.play(Write(computation, run_time = 2)) def wander_on_inner_sphere(self): self.wait(10) def poke_outside_outer_box(self): self.wind_down_ambient_movement() self.reset_dials([np.sqrt(10)-1]) words = TextMobject( "Outside the \\emph{outer} \\\\", "bounding box!" ) words.to_edge(LEFT) words.set_color(RED) arrow = Arrow( words.get_top(), self.sliders[0].dial, path_arc = -np.pi/3, color = words.get_color() ) self.play( Write(words, run_time = 2), ShowCreation(arrow) ) self.wait(3) class TwoDOuterBox(TwoDInnerSphereTouchingBox): def construct(self): words = TextMobject("$4 \\!\\times\\! 4$ outer bounding box") words.next_to(self.plane, UP) words.set_color(MAROON_B) line = Line( self.plane.coords_to_point(-2, -2), self.plane.coords_to_point(2, 2), ) box = Square(color = words.get_color()) box.replace(line, stretch = True) box.set_stroke(width = 8) self.play( Write(words), ShowCreation(box), self.randy.change, "pondering", ) self.wait(3) self.outer_box = box class ThreeDOuterBoundingBox(ExternallyAnimatedScene): pass class ThreeDOuterBoundingBoxWords(Scene): def construct(self): words = TextMobject( "$4 \\!\\times\\! 4\\!\\times\\! 4$ outer\\\\", "bounding box" ) words.set_width(FRAME_WIDTH-1) words.to_edge(DOWN) words.set_color(MAROON_B) self.play(Write(words)) self.wait(4) class FaceDistanceDoesntDependOnDimension(TwoDOuterBox): def construct(self): self.force_skipping() TwoDOuterBox.construct(self) self.randy.change("confused") self.revert_to_original_skipping_status() line = Line( self.plane.coords_to_point(0, 0), self.outer_box.get_right(), buff = 0, stroke_width = 6, color = YELLOW ) length_words = TextMobject("Always 2, in all dimensions") length_words.next_to(self.plane, RIGHT, MED_LARGE_BUFF, UP) arrow = Arrow(length_words[4].get_bottom(), line.get_center()) self.play(ShowCreation(line)) self.play( Write(length_words), ShowCreation(arrow) ) self.play(self.randy.change, "thinking") self.wait(3) class TenDCornerIsVeryFarAway(TenDBoxExampleWithSliders): CONFIG = { "center_point" : np.zeros(10) } def construct(self): self.show_re_rects() def show_re_rects(self): re_rects = VGroup() for slider in self.sliders: rect = Rectangle( width = 2*slider.tick_size, height = slider.unit_size, stroke_width = 0, fill_color = GREEN, fill_opacity = 0.75, ) rect.move_to(slider.number_to_point(0), DOWN) re_rects.add(rect) rect.save_state() rect.stretch_to_fit_height(0) rect.move_to(rect.saved_state, DOWN) self.set_to_vector(np.zeros(10)) self.play( LaggedStartMap( ApplyMethod, re_rects, lambda m : (m.restore,), lag_ratio = 0.3, ), LaggedStartMap( ApplyMethod, self.sliders, lambda m : (m.set_value, 1), lag_ratio = 0.3, ), run_time = 10, ) self.wait() class InnerRadiusIsUnbounded(TeacherStudentsScene): def construct(self): self.teacher_says("Inner radius \\\\ is unbounded") self.change_student_modes(*["erm"]*3) self.wait(3) class ProportionOfSphereInBox(GraphScene): CONFIG = { "x_axis_label" : "Dimension", "y_axis_label" : "", "y_max" : 1.5, "y_min" : 0, "y_tick_frequency" : 0.25, "y_labeled_nums" : np.linspace(0.25, 1, 4), "x_min" : 0, "x_max" : 50, "x_tick_frequency" : 5, "x_labeled_nums" : list(range(10, 50, 10)), "num_graph_anchor_points" : 100, } def construct(self): self.setup_axes() title = TextMobject( "Proportion of inner sphere \\\\ inside box" ) title.next_to(self.y_axis, RIGHT, MED_SMALL_BUFF, UP) self.add(title) graph = self.get_graph(lambda x : np.exp(0.1*(9-x))) max_y = self.coords_to_point(0, 1)[1] too_high = graph.points[:,1] > max_y graph.points[too_high, 1] = max_y footnote = TextMobject(""" \\begin{flushleft} *I may or may not have used an easy-to-compute \\\\ but not-totally-accurate curve here, due to \\\\ the surprising difficulty in computing the real \\\\ proportion :) \\end{flushleft} """,) footnote.scale(0.75) footnote.next_to( graph.point_from_proportion(0.3), UP+RIGHT, SMALL_BUFF ) footnote.set_color(YELLOW) self.play(ShowCreation(graph, run_time = 5, rate_func=linear)) self.wait() self.add(footnote) self.wait(0.25) class ShowingToFriend(PiCreatureScene, SliderScene): CONFIG = { "n_sliders" : 10, "ambient_acceleration_magnitude" : 3.0, "seconds_to_blink" : 4, } def setup(self): PiCreatureScene.setup(self) SliderScene.setup(self) self.sliders.scale(0.75) self.sliders.next_to( self.morty.get_corner(UP+LEFT), UP, MED_LARGE_BUFF ) self.initialize_ambiant_slider_movement() def construct(self): morty, randy = self.morty, self.randy self.play(morty.change, "raise_right_hand", self.sliders) self.play(randy.change, "happy", self.sliders) self.wait(7) self.play(randy.change, "skeptical", morty.eyes) self.wait(3) self.play(randy.change, "thinking", self.sliders) self.wait(6) ### def create_pi_creatures(self): self.morty = Mortimer() self.morty.to_edge(DOWN).shift(4*RIGHT) self.randy = Randolph() self.randy.to_edge(DOWN).shift(4*LEFT) return VGroup(self.morty, self.randy) def non_blink_wait(self, time = 1): SliderScene.wait(self, time) class QuestionsFromStudents(TeacherStudentsScene): def construct(self): self.student_says( "Is 10-dimensional \\\\ space real?", target_mode = "sassy", run_time = 2, ) self.wait() self.teacher_says( "No less real \\\\ than reals", target_mode = "shruggie", content_introduction_class = FadeIn, ) self.wait(2) self.student_says( "How do you think \\\\ about volume?", student_index = 0, content_introduction_class = FadeIn, ) self.wait() self.student_says( "How do cubes work?", student_index = 2, run_time = 2, ) self.wait(2) class FunHighDSpherePhenomena(Scene): def construct(self): title = TextMobject( "Fun high-D sphere phenomena" ) title.to_edge(UP) title.set_color(BLUE) h_line = Line(LEFT, RIGHT).scale(5) h_line.next_to(title, DOWN) self.add(title, h_line) items = VGroup(*list(map(TextMobject, [ "$\\cdot$ Most volume is near the equator", "$\\cdot$ Most volume is near the surface", "$\\cdot$ Sphere packing in 8 dimensions", "$\\cdot$ Sphere packing in 24 dimensions", ]))) items.arrange( DOWN, buff = MED_LARGE_BUFF, aligned_edge = LEFT ) items.next_to(h_line, DOWN) for item in items: self.play(LaggedStartMap(FadeIn, item, run_time = 2)) self.wait() class TODOBugOnSurface(TODOStub): CONFIG = { "message" : "Bug on surface" } class CoordinateFree(PiCreatureScene): def construct(self): plane = NumberPlane(x_radius = 2.5, y_radius = 2.5) plane.add_coordinates() plane.to_corner(UP+LEFT) self.add(plane) circles = VGroup(*[ Circle(color = YELLOW).move_to( plane.coords_to_point(*coords) ) for coords in it.product(*2*[[-1, 1]]) ]) inner_circle = Circle( radius = np.sqrt(2)-1, color = GREEN ).move_to(plane.coords_to_point(0, 0)) self.add_foreground_mobjects(circles, inner_circle) self.play(PiCreatureSays( self.pi_creature, "Lose the \\\\ coordinates!", target_mode = "hooray" )) self.play(FadeOut(plane, run_time = 2)) self.wait(3) class Skeptic(TeacherStudentsScene, SliderScene): def setup(self): SliderScene.setup(self) TeacherStudentsScene.setup(self) self.sliders.scale(0.7) self.sliders.next_to(self.teacher, UP, aligned_edge = LEFT) self.sliders.to_edge(UP) self.initialize_ambiant_slider_movement() def construct(self): analytic_thought = VGroup(TextMobject("No different from")) equation = TexMobject( "x", "^2 + ", "y", "^2 + ", "z", "^2 + ", "w", "^2 = 1" ) variables = VGroup(*[ equation.get_part_by_tex(tex) for tex in "xyzw" ]) slider_labels = VGroup(*[ slider.label for slider in self.sliders ]) equation.next_to(analytic_thought, DOWN) analytic_thought.add(equation) all_real_estate_ticks = VGroup(*it.chain(*[ slider.real_estate_ticks for slider in self.sliders ])) box = Square(color = RED) box.next_to(self.sliders, LEFT) line = Line(box.get_center(), box.get_corner(UP+RIGHT)) line.set_color(YELLOW) self.student_says( analytic_thought, student_index = 0, target_mode = "sassy", added_anims = [self.teacher.change, "guilty"] ) self.wait(2) equation.remove(*variables) self.play(ReplacementTransform(variables, slider_labels)) self.play( self.teacher.change, "pondering", slider_labels, RemovePiCreatureBubble( self.students[0], target_mode = "hesitant" ), ) self.wait(4) bubble = self.teacher.get_bubble( "It's much \\\\ more playful!", bubble_class = SpeechBubble ) bubble.resize_to_content() VGroup(bubble, bubble.content).next_to(self.teacher, UP+LEFT) self.play( self.teacher.change, "hooray", ShowCreation(bubble), Write(bubble.content) ) self.wait(3) self.play( RemovePiCreatureBubble( self.teacher, target_mode = "raise_right_hand", look_at_arg = self.sliders ), *[ ApplyMethod(pi.change, "pondering") for pi in self.students ] ) self.play(Animation(self.sliders), LaggedStartMap( ApplyMethod, all_real_estate_ticks, lambda m : (m.shift, SMALL_BUFF*LEFT), rate_func = wiggle, lag_ratio = 0.3, run_time = 4, )) self.play( ShowCreation(box), self.teacher.change, "happy" ) self.play(ShowCreation(line)) self.wait(3) ##### def non_blink_wait(self, time = 1): SliderScene.wait(self, time) class ClipFrom4DBoxExampleTODO(TODOStub): CONFIG = { "message" : "Clip from 4d box example" } class JustBecauseYouCantVisualize(Scene): def construct(self): phrase = "\\raggedright " phrase += "Just because you can't visualize\\\\ " phrase += "something doesn't mean you can't\\\\ " phrase += "still think about it visually." phrase_mob = TextMobject(*phrase.split(" ")) phrase_mob.set_color_by_tex("visual", YELLOW) phrase_mob.next_to(ORIGIN, UP) for part in phrase_mob: self.play(LaggedStartMap( FadeIn, part, run_time = 0.05*len(part) )) self.wait(2) class Announcements(TeacherStudentsScene): def construct(self): title = TextMobject("Announcements") title.scale(1.5) title.to_edge(UP, buff = MED_SMALL_BUFF) h_line = Line(LEFT, RIGHT).scale(3) h_line.next_to(title, DOWN) self.add(title, h_line) items = VGroup(*list(map(TextMobject, [ "$\\cdot$ Where to learn more", "$\\cdot$ Q\\&A Followup (podcast!)", ]))) items.arrange(DOWN, aligned_edge = LEFT) items.next_to(h_line, DOWN) self.play( Write(items[0], run_time = 2), ) self.play(*[ ApplyMethod(pi.change, "hooray", items) for pi in self.pi_creatures ]) self.play(Write(items[1], run_time = 2)) self.wait(2) class Promotion(PiCreatureScene): CONFIG = { "seconds_to_blink" : 5, } def construct(self): url = TextMobject("https://brilliant.org/3b1b/") url.to_corner(UP+LEFT) rect = Rectangle(height = 9, width = 16) rect.set_height(5.5) rect.next_to(url, DOWN) rect.to_edge(LEFT) self.play( Write(url), self.pi_creature.change, "raise_right_hand" ) self.play(ShowCreation(rect)) self.wait(2) self.change_mode("thinking") self.wait() self.look_at(url) self.wait(10) self.change_mode("happy") self.wait(10) self.change_mode("raise_right_hand") self.wait(10) self.remove(rect) self.play( url.next_to, self.pi_creature, UP+LEFT ) url_rect = SurroundingRectangle(url) self.play(ShowCreation(url_rect)) self.play(FadeOut(url_rect)) self.wait(3) class BrilliantGeometryQuiz(ExternallyAnimatedScene): pass class BrilliantScrollThroughCourses(ExternallyAnimatedScene): pass class Podcast(TeacherStudentsScene): def construct(self): title = TextMobject("Podcast!") title.scale(1.5) title.to_edge(UP) title.shift(FRAME_X_RADIUS*LEFT/2) self.add(title) q_and_a = TextMobject("Q\\&A Followup") q_and_a.next_to(self.teacher.get_corner(UP+LEFT), UP, LARGE_BUFF) self.play( LaggedStartMap( ApplyMethod, self.pi_creatures, lambda pi : (pi.change, "hooray", title) ), Write(title) ) self.wait(5) self.play( Write(q_and_a), self.teacher.change, "raise_right_hand", ) self.wait(4) class HighDPatreonThanks(PatreonThanks): CONFIG = { "specific_patrons" : [ "Desmos", "Burt Humburg", "CrypticSwarm", "Juan Benet", "Ali Yahya", "William", "Mayank M. Mehrotra", "Lukas Biewald", "Samantha D. Suplee", "James Park", "Yana Chernobilsky", "Kaustuv DeBiswas", "Kathryn Schmiedicke", "Yu Jun", "dave nicponski", "Damion Kistler", "Markus Persson", "Yoni Nazarathy", "Corey Ogburn", "Ed Kellett", "Joseph John Cox", "Dan Buchoff", "Luc Ritchie", "Erik Sundell", "Xueqi Li", "David Stork", "Tianyu Ge", "Ted Suzman", "Amir Fayazi", "Linh Tran", "Andrew Busey", "Michael McGuffin", "John Haley", "Ankalagon", "Eric Lavault", "Tomohiro Furusawa", "Boris Veselinovich", "Julian Pulgarin", "Jeff Linse", "Cooper Jones", "Ryan Dahl", "Mark Govea", "Robert Teed", "Jason Hise", "Meshal Alshammari", "Bernd Sing", "Nils Schneider", "James Thornton", "Mustafa Mahdi", "Mathew Bramson", "Jerry Ling", "Vecht", "Shimin Kuang", "Rish Kundalia", "Achille Brighton", "Ripta Pasay", ] } class Thumbnail(SliderScene): CONFIG = { "n_sliders" : 10, } def construct(self): for slider in self.sliders: self.remove(slider.label) slider.remove(slider.label) vect = np.random.random(10) - 0.5 vect /= get_norm(vect) self.set_to_vector(vect) title = TextMobject("10D Sphere?") title.scale(2) title.to_edge(UP) self.add(title)