from big_ol_pile_of_manim_imports import * from active_projects.shadows import * # Abstract scenes class Cylinder(Sphere): """ Inherits from sphere so as to be as aligned as possible for transformations """ def func(self, u, v): return np.array([ np.cos(v), np.sin(v), np.cos(u) ]) class UnwrappedCylinder(Cylinder): def func(self, u, v): return np.array([ v - PI, -self.radius, np.cos(u) ]) class ParametricDisc(Sphere): CONFIG = { "u_min": 0, "u_max": 1, "stroke_width": 0, "checkerboard_colors": [BLUE_D], } def func(self, u, v): return np.array([ u * np.cos(v), u * np.sin(v), 0, ]) class SphereCylinderScene(SpecialThreeDScene): CONFIG = { "cap_config": { "stroke_width": 1, "stroke_color": WHITE, "fill_color": BLUE_D, "fill_opacity": 1, } } def get_cylinder(self): return Cylinder(**self.sphere_config) def get_cylinder_caps(self): R = self.sphere_config["radius"] caps = VGroup(*[ Circle( radius=R, **self.cap_config, ).shift(R * vect) for vect in [IN, OUT] ]) caps.set_shade_in_3d(True) return caps def get_unwrapped_cylinder(self): return UnwrappedCylinder(**self.sphere_config) def get_xy_plane(self): pass def get_ghost_surface(self, surface): result = surface.copy() result.set_fill(BLUE_E, opacity=0.5) result.set_stroke(WHITE, width=0.5, opacity=0.5) return result def project_point(self, point): radius = self.sphere_config["radius"] result = np.array(point) result[:2] = normalize(result[:2]) * radius return result def project_mobject(self, mobject): return mobject.apply_function(self.project_point) # Scenes for video class AskAboutShadowRelation(SpecialThreeDScene): CONFIG = { "R_color": YELLOW, "space_out_factor": 1.15, } def construct(self): self.show_surface_area() self.show_four_circles() self.ask_why() self.show_all_pieces() def show_surface_area(self): sphere = self.get_sphere() sphere.set_fill(BLUE_E, opacity=0.5) sphere.add_updater( lambda s, dt: s.rotate(0.1 * dt, axis=OUT) ) pieces = sphere.deepcopy() pieces.space_out_submobjects(1.5) pieces.shift(IN) pieces.set_color(GREEN) # radial_line = Line(ORIGIN, sphere.get_right()) # R_label = TexMobject("R") # R_label.set_color(BLUE) # R_label.rotate(90 * DEGREES, RIGHT) # R_label.next_to(radial_line, OUT, SMALL_BUFF) sa_equation = TexMobject( "\\text{Surface area} = 4\\pi R^2", tex_to_color_map={"R": BLUE} ) sa_equation.scale(1.5) sa_equation.to_edge(UP) self.set_camera_orientation( phi=70 * DEGREES, theta=-90 * DEGREES, ) self.add_fixed_in_frame_mobjects(sa_equation) self.play( Write(sphere, stroke_width=1), FadeInFromDown(sa_equation), # ShowCreation(radial_line), # FadeInFrom(R_label, IN), ) # self.play( # Transform( # sphere, pieces, # rate_func=there_and_back_with_pause, # run_time=2 # ) # ) self.play(LaggedStart( UpdateFromAlphaFunc, sphere, lambda mob: (mob, lambda m, a: m.set_fill( color=interpolate_color(BLUE_E, YELLOW, a), opacity=interpolate(0.5, 1, a) )), rate_func=there_and_back, lag_ratio=0.1, run_time=4 )) self.play( sphere.scale, 0.75, sphere.shift, 3 * LEFT, sa_equation.shift, 3 * LEFT, ) self.wait(2) self.sphere = sphere self.sa_equation = sa_equation def show_four_circles(self): sphere = self.sphere shadow = Circle( radius=sphere.get_width() / 2, stroke_color=WHITE, stroke_width=1, fill_color=BLUE_E, fill_opacity=1, ) radial_line = Line( shadow.get_center(), shadow.get_right(), color=YELLOW ) R_label = TexMobject("R").set_color(self.R_color) R_label.scale(0.8) R_label.next_to(radial_line, DOWN, SMALL_BUFF) shadow.add(radial_line, R_label) shadow.move_to( self.camera.transform_points_pre_display(sphere, [sphere.get_center()])[0] ) shadows = VGroup(*[shadow.copy() for x in range(4)]) shadows.arrange_submobjects_in_grid(buff=MED_LARGE_BUFF) shadows.to_edge(RIGHT) area_label = TexMobject( "\\pi R^2", tex_to_color_map={"R": self.R_color} ) area_label.scale(1.2) area_labels = VGroup(*[ area_label.copy().move_to(interpolate( mob.get_center(), mob.get_top(), 0.5 )) for mob in shadows ]) # shadow.move_to(sphere) self.add_fixed_in_frame_mobjects(shadow) self.play(DrawBorderThenFill(shadow)) anims = [] for new_shadow in shadows: old_shadow = shadow.copy() self.add_fixed_in_frame_mobjects(old_shadow) anims.append(Transform( old_shadow, new_shadow, remover=True )) self.remove(shadow) self.play(*anims) self.add_fixed_in_frame_mobjects(shadows, area_labels) self.play(LaggedStart(FadeInFromLarge, area_labels)) self.wait() self.shadows = shadows self.shadow_area_labels = area_labels def ask_why(self): pass def show_all_pieces(self): shadows = self.shadows area_labels = self.shadow_area_labels sphere = self.sphere shadow_pieces_group = VGroup() for shadow in shadows: pieces = ParametricSurface( func=lambda u, v: np.array([ u * np.cos(TAU * v), u * np.sin(TAU * v), 0, ]), resolution=(12, 24) ) pieces.replace(shadow) pieces.match_style(sphere) shadow_pieces_group.add(pieces) self.add_fixed_in_frame_mobjects(shadow_pieces_group) self.play( FadeOut(shadows), FadeOut(area_labels), FadeIn(shadow_pieces_group) ) self.show_area_pieces(sphere) self.wait() self.show_area_pieces(*shadow_pieces_group) self.wait(2) self.unshow_area_pieces(sphere, *shadow_pieces_group) self.wait(3) def show_area_pieces(self, *mobjects): for mob in mobjects: mob.generate_target() mob.target.space_out_submobjects(self.space_out_factor) self.play(*map(MoveToTarget, mobjects)) self.play(*[ LaggedStart( ApplyMethod, mob, lambda m: (m.set_fill, YELLOW, 1), rate_func=there_and_back, lag_ratio=0.25, run_time=1.5 ) for mob in mobjects ]) def unshow_area_pieces(self, *mobjects): self.play(*[ ApplyMethod( mob.space_out_submobjects, 1.0 / self.space_out_factor ) for mob in mobjects ]) class ButWhy(TeacherStudentsScene): def construct(self): self.student_says( "But why?!?", student_index=2, target_mode="angry", bubble_kwargs={"direction": LEFT}, ) self.play( self.students[0].change, "pondering", self.screen, self.students[1].change, "pondering", self.screen, self.teacher.change, "guilty", self.screen, ) self.wait(3) class TryFittingCirclesDirectly(ExternallyAnimatedScene): pass class PreviewTwoMethods(Scene): def construct(self): pass # TODO class MapSphereOntoCylinder(SphereCylinderScene): def construct(self): sphere = self.get_sphere() sphere_ghost = self.get_ghost_surface(sphere) sphere_ghost.set_stroke(width=0) axes = self.get_axes() cylinder = self.get_cylinder() cylinder.set_fill(opacity=0.75) radius = cylinder.get_width() / 2 self.add(axes, sphere_ghost, sphere) self.wait() self.begin_ambient_camera_rotation() self.move_camera( **self.default_angled_camera_position, run_time=2, ) self.wait(2) self.play( ReplacementTransform(sphere, cylinder), run_time=3 ) self.wait(3) # Get rid of caps caps = self.get_cylinder_caps() caps[1].set_shade_in_3d(False) label = TextMobject("Label") label.scale(1.5) label.stretch(0.8, 0) label.rotate(90 * DEGREES, RIGHT) label.rotate(90 * DEGREES, OUT) label.shift(np.log(radius + SMALL_BUFF) * RIGHT) label.apply_complex_function(np.exp) label.rotate(90 * DEGREES, IN, about_point=ORIGIN) label.shift(OUT) label.set_background_stroke(width=0) self.play(FadeIn(caps)) self.wait() self.play( caps.space_out_submobjects, 2, caps.fade, 1, remover=True ) self.play(Write(label)) self.wait(2) self.play(FadeOut(label)) # Unwrap unwrapped_cylinder = self.get_unwrapped_cylinder() unwrapped_cylinder.set_fill(opacity=0.75) self.play( ReplacementTransform(cylinder, unwrapped_cylinder), run_time=3 ) self.stop_ambient_camera_rotation() self.move_camera( phi=90 * DEGREES, theta=-90 * DEGREES, ) # Show dimensions stroke_width = 5 top_line = Line( PI * radius * LEFT + radius * OUT, PI * radius * RIGHT + radius * OUT, color=YELLOW, stroke_width=stroke_width, ) side_line = Line( PI * radius * LEFT + radius * OUT, PI * radius * LEFT + radius * IN, color=RED, stroke_width=stroke_width, ) lines = VGroup(top_line, side_line) lines.shift(radius * DOWN) two_pi_R = TexMobject("2\\pi R") two_R = TexMobject("2R") texs = VGroup(two_pi_R, two_R) for tex in texs: tex.scale(1.5) tex.rotate(90 * DEGREES, RIGHT) two_pi_R.next_to(top_line, OUT) two_R.next_to(side_line, RIGHT) self.play(LaggedStart( DrawBorderThenFill, VGroup(*lines, *texs) )) self.wait() class ShowProjection(SphereCylinderScene): CONFIG = { # "default_angled_camera_position": { # "theta": -155 * DEGREES, # } } def construct(self): self.setup_shapes() self.show_many_tiny_rectangles() self.project_all_rectangles() self.focus_on_one() self.label_sides() self.show_length_scaled_up() self.show_height_scaled_down() def setup_shapes(self): self.sphere = self.get_sphere() self.cylinder = self.get_cylinder() self.ghost_sphere = self.get_ghost_surface(self.sphere) self.ghost_sphere.scale(0.99) self.ghost_cylinder = self.get_ghost_surface(self.cylinder) self.ghost_cylinder.set_stroke(width=0) self.add(self.get_axes()) self.set_camera_to_default_position() self.begin_ambient_camera_rotation() def show_many_tiny_rectangles(self): ghost_sphere = self.ghost_sphere pieces = self.sphere.copy() random.shuffle(pieces.submobjects) for piece in pieces: piece.save_state() pieces.space_out_submobjects(2) pieces.fade(1) self.add(ghost_sphere) self.play(LaggedStart(Restore, pieces)) self.wait() self.pieces = pieces def project_all_rectangles(self): pieces = self.pieces for piece in pieces: piece.save_state() piece.generate_target() self.project_mobject(piece.target) piece.target.set_fill(opacity=0.5) example_group = self.get_example_group([1, -1, 2]) proj_lines = example_group[1] self.example_group = example_group self.play(*map(ShowCreation, proj_lines)) self.play( LaggedStart(MoveToTarget, pieces), ) self.wait() def focus_on_one(self): ghost_cylinder = self.ghost_cylinder pieces = self.pieces example_group = self.example_group original_rect, rect_proj_lines, rect = example_group equation = self.get_equation(rect) lhs, equals, rhs = equation[:3] lhs.save_state() rhs.save_state() self.play( FadeIn(ghost_cylinder), FadeOut(pieces), FadeIn(original_rect), ) self.play(TransformFromCopy(original_rect, rect)) self.wait() self.add_fixed_in_frame_mobjects(lhs, equals, rhs) self.move_fixed_in_frame_mob_to_unfixed_mob(lhs, original_rect) self.move_fixed_in_frame_mob_to_unfixed_mob(rhs, rect) self.play( Restore(lhs), Restore(rhs), FadeInFromDown(equals), ) self.wait(5) self.equation = equation self.example_group = example_group def label_sides(self): sphere = self.sphere equation = self.equation l_brace, h_brace = equation.braces length_label, height_label = equation.labels u_values, v_values = sphere.get_u_values_and_v_values() radius = sphere.radius lat_lines = VGroup(*[ ParametricFunction( lambda t: radius * sphere.func(u, t), t_min=sphere.v_min, t_max=sphere.v_max, ) for u in u_values ]) lon_lines = VGroup(*[ ParametricFunction( lambda t: radius * sphere.func(t, v), t_min=sphere.u_min, t_max=sphere.u_max, ) for v in v_values ]) for lines in lat_lines, lon_lines: for line in lines: line.add(DashedMobject(line, spacing=-1)) line.set_points([]) line.set_stroke(width=2) lines.set_shade_in_3d(True) lat_lines.set_color(RED) lon_lines.set_color(PINK) self.play( *map(ShowCreationThenDestruction, lat_lines), run_time=2 ) self.add_fixed_in_frame_mobjects(l_brace, length_label) self.play( GrowFromCenter(l_brace), Write(length_label), ) self.wait() self.play( *map(ShowCreationThenDestruction, lon_lines), run_time=2 ) self.add_fixed_in_frame_mobjects(h_brace, height_label) self.play( GrowFromCenter(h_brace), Write(height_label), ) self.wait(2) def show_length_scaled_up(self): ghost_sphere = self.ghost_sphere example_group = self.example_group equation = self.equation equation.save_state() new_example_groups = [ self.get_example_group([1, -1, z]) for z in [6, 0.25] ] r1, lines, r2 = example_group self.stop_ambient_camera_rotation() self.move_camera( phi=65 * DEGREES, theta=-80 * DEGREES, added_anims=[ ghost_sphere.set_stroke, {"opacity": 0.1}, lines.set_stroke, {"width": 3}, ] ) for eg in new_example_groups: eg[1].set_stroke(width=3) self.show_length_stretch_of_rect(example_group) all_example_groups = [example_group, *new_example_groups] for eg1, eg2 in zip(all_example_groups, all_example_groups[1:]): if eg1 is new_example_groups[0]: self.move_camera( phi=70 * DEGREES, theta=-110 * DEGREES ) self.remove(eg1) self.play( ReplacementTransform(eg1.deepcopy(), eg2), Transform( equation, self.get_equation(eg2[2]) ) ) if eg1 is example_group: self.move_camera( phi=0, theta=-90 * DEGREES, ) self.show_length_stretch_of_rect(eg2) self.play( ReplacementTransform(all_example_groups[-1], example_group), Restore(equation) ) def show_length_stretch_of_rect(self, example_group): s_rect, proj_lines, c_rect = example_group rects = VGroup(s_rect, c_rect) line1, line2 = lines = VGroup(*[ Line(m.get_anchors()[1], m.get_anchors()[2]) for m in rects ]) lines.set_stroke(YELLOW, 5) lines.set_shade_in_3d(True) proj_lines_to_fade = VGroup( proj_lines[0], proj_lines[3], ) self.play( FadeIn(lines[0]), FadeOut(rects), FadeOut(proj_lines_to_fade) ) for x in range(3): anims = [] if lines[1] in self.mobjects: anims.append(FadeOut(lines[1])) self.play( TransformFromCopy(lines[0], lines[1]), *anims ) self.play( FadeOut(lines), FadeIn(rects), FadeIn(proj_lines_to_fade), ) self.remove(rects, proj_lines_to_fade) self.add(example_group) def show_height_scaled_down(self): ghost_sphere = self.ghost_sphere ghost_cylinder = self.ghost_cylinder example_group = self.example_group equation = self.equation r1, lines, r2 = example_group to_fade = VGroup(*[ mob for mob in it.chain(ghost_sphere, ghost_cylinder) if np.dot(mob.get_center(), [1, 1, 0]) < 0 ]) to_fade.save_state() new_example_groups = [ self.get_example_group([1, -1, z]) for z in [6, 0.25] ] for eg in new_example_groups: eg[::2].set_stroke(YELLOW, 2) eg.set_stroke(width=1) all_example_groups = VGroup(example_group, *new_example_groups) self.play( to_fade.shift, IN, to_fade.fade, 1, remover=True ) self.move_camera( phi=85 * DEGREES, theta=-135 * DEGREES, added_anims=[ lines.set_stroke, {"width": 1}, r1.set_stroke, YELLOW, 2, 1, r2.set_stroke, YELLOW, 2, 1, ] ) self.show_shadow(example_group) for eg1, eg2 in zip(all_example_groups, all_example_groups[1:]): self.remove(*eg1.get_family()) self.play( ReplacementTransform(eg1.deepcopy(), eg2), Transform( equation, self.get_equation(eg2[2]) ) ) self.show_shadow(eg2) self.move_camera( phi=70 * DEGREES, theta=-115 * DEGREES, added_anims=[ ReplacementTransform( all_example_groups[-1], example_group, ), Restore(equation), ] ) self.begin_ambient_camera_rotation() self.play(Restore(to_fade)) self.wait(5) def show_shadow(self, example_group): s_rect, lines, c_rect = example_group for x in range(3): self.play(TransformFromCopy(s_rect, c_rect)) # def get_projection_lines(self, piece): result = VGroup() radius = self.sphere_config["radius"] for corner in piece.get_anchors()[:-1]: start = np.array(corner) end = np.array(corner) start[:2] = np.zeros(2) end[:2] = (radius + 0.03) * normalize(end[:2]) kwargs = { "color": YELLOW, "stroke_width": 0.5, } result.add(VGroup(*[ Line(p1, p2, **kwargs) for p1, p2 in [(start, corner), (corner, end)] ])) result.set_shade_in_3d(True) return result def get_equation(self, rect): length = get_norm(rect.get_anchors()[1] - rect.get_anchors()[0]) height = get_norm(rect.get_anchors()[2] - rect.get_anchors()[1]) lhs = Rectangle(width=length, height=height) rhs = Rectangle(width=height, height=length) eq_rects = VGroup(lhs, rhs) eq_rects.set_stroke(width=0) eq_rects.set_fill(YELLOW, 1) eq_rects.scale(2) equals = TexMobject("=") equation = VGroup(lhs, equals, rhs) equation.arrange_submobjects(RIGHT) equation.to_corner(UR) brace = Brace(Line(ORIGIN, 0.5 * RIGHT), DOWN) l_brace = brace.copy().match_width(lhs, stretch=True) h_brace = brace.copy().rotate(-90 * DEGREES) h_brace.match_height(lhs, stretch=True) l_brace.next_to(lhs, DOWN, buff=SMALL_BUFF) h_brace.next_to(lhs, LEFT, buff=SMALL_BUFF) braces = VGroup(l_brace, h_brace) length_label = TextMobject("Length") height_label = TextMobject("Height") labels = VGroup(length_label, height_label) labels.scale(0.75) length_label.next_to(l_brace, DOWN, SMALL_BUFF) height_label.next_to(h_brace, LEFT, SMALL_BUFF) equation.braces = braces equation.labels = labels equation.add(braces, labels) return equation def move_fixed_in_frame_mob_to_unfixed_mob(self, m1, m2): phi = self.camera.phi_tracker.get_value() theta = self.camera.theta_tracker.get_value() target = m2.get_center() target = rotate_vector(target, -theta - 90 * DEGREES, OUT) target = rotate_vector(target, -phi, RIGHT) m1.move_to(target) m1.scale(0.1) m1.shift(SMALL_BUFF * UR) return m1 def get_example_group(self, vect): pieces = self.pieces rect = pieces[np.argmax([ np.dot(r.saved_state.get_center(), vect) for r in pieces ])].saved_state.copy() rect_proj_lines = self.get_projection_lines(rect) rect.set_fill(YELLOW, 1) original_rect = rect.copy() self.project_mobject(rect) rect.shift( 0.001 * np.array([*rect.get_center()[:2], 0]) ) result = VGroup(original_rect, rect_proj_lines, rect) result.set_shade_in_3d(True) return result class SlantedShadowSquishing(ShowShadows): CONFIG = { "num_reorientations": 4, "random_seed": 2, } def setup(self): ShowShadows.setup(self) self.surface_area_label.fade(1) self.shadow_area_label.fade(1) self.shadow_area_decimal.fade(1) def construct(self): # Show creation self.set_camera_orientation( phi=70 * DEGREES, theta=-150 * DEGREES, ) self.begin_ambient_camera_rotation(0.01) square = self.obj3d.deepcopy() square.clear_updaters() shadow = updating_mobject_from_func(lambda: get_shadow(square)) # Reorient self.add(square, shadow) for n in range(self.num_reorientations): angle = 40 * DEGREES self.play( Rotate(square, angle, axis=RIGHT), run_time=2 ) def get_object(self): square = Square() square.set_shade_in_3d(True) square.set_height(2) square.set_stroke(WHITE, 0.5) square.set_fill(BLUE_C, 1) return square class JustifyLengthStretch(ShowProjection): CONFIG = { "R_color": RED, "d_color": WHITE, "d_ambiguity_iterations": 4, } def construct(self): self.setup_shapes() self.add_ghosts() self.add_example_group() self.cut_cross_section() self.label_R() self.label_d() self.show_similar_triangles() def add_ghosts(self): self.add(self.ghost_sphere, self.ghost_cylinder) def add_example_group(self): self.pieces = self.sphere for piece in self.pieces: piece.save_state() self.example_group = self.get_example_group([1, 0.1, 1.5]) self.add(self.example_group) self.set_camera_orientation(theta=-45 * DEGREES) def cut_cross_section(self): sphere = self.ghost_sphere cylinder = self.ghost_cylinder to_fade = VGroup(*[ mob for mob in it.chain(sphere, cylinder) if np.dot(mob.get_center(), DOWN) > 0 ]) self.lost_hemisphere = to_fade to_fade.save_state() circle = Circle( stroke_width=2, stroke_color=PINK, radius=self.sphere.radius ) circle.rotate(90 * DEGREES, RIGHT) self.circle = circle self.example_group.set_stroke(YELLOW, 1) self.stop_ambient_camera_rotation() self.play( Rotate( to_fade, -PI, axis=OUT, about_point=sphere.get_left(), run_time=2 ), VFadeOut(to_fade, run_time=2), FadeIn(circle), ) self.move_camera( phi=80 * DEGREES, theta=-85 * DEGREES, ) def label_R(self): circle = self.circle R_line = Line(ORIGIN, circle.get_right()) R_line.set_color(self.R_color) R_label = TexMobject("R") R_label.next_to(R_line, IN + DOWN) self.add_fixed_orientation_mobjects(R_label) self.play( ShowCreation(R_line), FadeInFrom(R_label, IN), ) self.wait() self.R_line = R_line self.R_label = R_label def label_d(self): example_group = self.example_group s_rect = example_group[0] d_line = self.get_d_line( s_rect.get_corner(IN + RIGHT + DOWN) ) alt_d_line = self.get_d_line( s_rect.get_corner(OUT + LEFT + DOWN) ) d_label = TexMobject("d") d_label.next_to(d_line, IN + DOWN) self.add_fixed_orientation_mobjects(d_label) self.play( ShowCreation(d_line), FadeInFrom(d_label, IN), ) self.wait() for x in range(self.d_ambiguity_iterations): to_fade_out = [d_line, alt_d_line][x % 2] to_fade_in = [d_line, alt_d_line][(x + 1) % 2] self.play( FadeIn(to_fade_in), FadeOut(to_fade_out), d_label.next_to, to_fade_in, IN + DOWN, ) self.d_line = d_line self.d_label = d_label def show_similar_triangles(self): d_line = self.d_line d_label = self.d_label R_line = self.R_line R_label = self.R_label example_group = self.example_group s_rect = example_group[0] p1 = s_rect.get_anchors()[1] p2 = s_rect.get_anchors()[2] p0 = np.array(p1) p0[:2] = np.zeros(2) triangle = Polygon(p0, p1, p2) triangle.set_stroke(width=0) triangle.set_fill(GREEN, opacity=1) base = Line(p1, p2) base.set_stroke(PINK, 3) big_triangle = Polygon( p0, self.project_point(p1), self.project_point(p2) ) big_triangle.set_stroke(width=0) big_triangle.set_fill(RED, opacity=1) equation = VGroup( triangle.copy(), TexMobject("\\sim"), big_triangle.copy() ) equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF) equation.to_corner(UL) eq_d = TexMobject("d").next_to(equation[0], DOWN, SMALL_BUFF) eq_R = TexMobject("R").next_to(equation[2], DOWN, SMALL_BUFF) self.move_camera( phi=0 * DEGREES, theta=-90 * DEGREES, distance=1000, added_anims=[ d_label.next_to, d_line, DOWN, SMALL_BUFF, FadeOut(R_label) ], run_time=2, ) self.play(FadeInFromLarge(triangle, 3)) self.wait() for x in range(2): self.play(ShowCreationThenDestruction(base)) self.wait() self.play(ShowCreation(d_line)) self.wait(2) self.play( TransformFromCopy(triangle, equation[0]), FadeIn(equation[1:]), FadeInFromDown(eq_d), FadeInFromDown(eq_R), ) self.wait() self.play( ReplacementTransform(triangle, big_triangle), FadeOut(d_label), FadeIn(R_label), ) self.add(R_line) R_line.set_color(WHITE) self.play(ShowCreation(R_line)) self.wait(3) self.add_fixed_in_frame_mobjects(equation, eq_d, eq_R) self.move_camera( phi=70 * DEGREES, theta=-70 * DEGREES, added_anims=[ big_triangle.set_fill, {"opacity": 0.25} ] ) self.begin_ambient_camera_rotation() lost_hemisphere = self.lost_hemisphere lost_hemisphere.restore() left_point = self.sphere.get_left() lost_hemisphere.rotate(-PI, axis=OUT, about_point=left_point) d_label.next_to(d_line, IN, buff=0.3) self.play( Rotate( lost_hemisphere, PI, axis=OUT, about_point=left_point, ), VFadeIn(lost_hemisphere), FadeOut(self.circle), FadeIn(d_label), R_line.set_color, self.R_color, ) self.wait(10) # def get_d_line(self, sphere_point): end = sphere_point start = np.array(end) start[:2] = np.zeros(2) d_line = Line(start, end) d_line.set_color(self.d_color) return d_line class LengthScaleLabel(Scene): def construct(self): text = TexMobject( "\\text{Length scale factor} =", "\\frac{R}{d}" ) self.play(Write(text)) self.wait() class TinierAndTinerRectangles(SphereCylinderScene): def construct(self): spheres = [ self.get_sphere( resolution=(12 * (2**n), 24 * (2**n)), stroke_width=0, ) for n in range(4) ] self.set_camera_to_default_position() self.begin_ambient_camera_rotation() self.add(spheres[0]) for s1, s2 in zip(spheres, spheres[1:]): self.wait() random.shuffle(s2.submobjects) for piece in s2: piece.save_state() s2.space_out_submobjects(1.2) s2.fade(1) for piece in s1: piece.add(VectorizedPoint(piece.get_center() / 2)) self.play( LaggedStart(Restore, s2) ) self.remove(s1) class LimitViewToCrossSection(JustifyLengthStretch): CONFIG = { "d_ambiguity_iterations": 0, } def construct(self): self.setup_shapes() self.add_ghosts() self.add_example_group() self.cut_cross_section() self.label_R() self.label_d() self.move_camera( phi=90 * DEGREES, theta=-90 * DEGREES, ) self.play( FadeOut(self.ghost_sphere), FadeOut(self.ghost_cylinder), ) class JustifyHeightSquish(MovingCameraScene): CONFIG = { "top_phi": 0.5242654422649652, "low_phi": 0.655081802831207, } def construct(self): pass