3b1b-manim/old_projects/sphere_area.py

3709 lines
107 KiB
Python
Raw Normal View History

2019-05-02 20:36:14 -07:00
from manimlib.imports import *
2018-11-24 12:17:01 -08:00
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,
}
}
2018-11-29 17:30:34 -08:00
def get_cylinder(self, **kwargs):
config = merge_dicts_recursively(self.sphere_config, kwargs)
2018-11-29 17:30:34 -08:00
return Cylinder(**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
2018-11-24 12:17:01 -08:00
class AskAboutShadowRelation(SpecialThreeDScene):
CONFIG = {
"R_color": YELLOW,
"space_out_factor": 1.15,
}
2018-11-24 12:17:01 -08:00
def construct(self):
self.show_surface_area()
self.show_four_circles()
self.ask_why()
self.show_all_pieces()
2018-11-24 12:17:01 -08:00
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,
)
2018-11-24 12:17:01 -08:00
self.add_fixed_in_frame_mobjects(sa_equation)
self.play(
Write(sphere, stroke_width=1),
2018-11-24 12:17:01 -08:00
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
# )
# )
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(
2018-11-24 12:17:01 -08:00
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
2018-11-24 12:17:01 -08:00
))
self.play(
sphere.scale, 0.75,
sphere.shift, 3 * LEFT,
sa_equation.shift, 3 * LEFT,
)
2018-11-24 12:17:01 -08:00
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)])
2019-02-04 14:54:25 -08:00
shadows.arrange_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)
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(FadeInFromLarge, area_labels))
self.wait()
2018-11-24 12:17:01 -08:00
self.shadows = shadows
self.shadow_area_labels = area_labels
def ask_why(self):
2018-11-24 12:17:01 -08:00
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(*[
2019-02-14 12:06:21 -08:00
LaggedStartMap(
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):
2018-12-26 11:37:07 -08:00
CONFIG = {
"student_mode": "raise_right_hand",
"question": "But why?",
"teacher_mode": "guilty"
}
def construct(self):
2018-12-26 11:37:07 -08:00
for student in self.students:
student.change("pondering", self.screen)
self.student_says(
2018-12-26 11:37:07 -08:00
"But why?",
student_index=2,
2018-12-26 11:37:07 -08:00
target_mode=self.student_mode,
bubble_kwargs={"direction": LEFT},
)
self.play(
2018-12-26 11:37:07 -08:00
self.teacher.change, self.teacher_mode, self.students[2]
)
2018-12-26 11:37:07 -08:00
self.wait(5)
class ButWhyHappy(ButWhy):
CONFIG = {
"teacher_mode": "happy"
}
class ButWhySassy(ButWhy):
CONFIG = {
"teacher_mode": "sassy"
}
class ButWhyHesitant(ButWhy):
CONFIG = {
"teacher_mode": "hesitant"
}
class ButWhyAngry(ButWhy):
CONFIG = {
"teacher_mode": "angry"
}
class TryFittingCirclesDirectly(ExternallyAnimatedScene):
pass
2018-12-26 11:37:07 -08:00
class PreviewTwoMethods(MovingCameraScene):
def construct(self):
2018-12-26 11:37:07 -08:00
thumbnails = Group(
ImageMobject("SphereSurfaceProof1"),
ImageMobject("SphereSurfaceProof2"),
)
for thumbnail in thumbnails:
thumbnail.set_width(FRAME_WIDTH / 2 - 1)
thumbnail.add_to_back(SurroundingRectangle(
thumbnail, buff=0,
stroke_color=WHITE,
stroke_width=5
))
2019-02-04 14:54:25 -08:00
thumbnails.arrange(RIGHT, buff=LARGE_BUFF)
2018-12-26 11:37:07 -08:00
title = TextMobject("Two proofs")
title.scale(2)
title.to_edge(UP)
frame = self.camera.frame
self.add(title)
for thumbnail in thumbnails:
self.play(FadeInFromDown(thumbnail))
self.wait()
for thumbnail in thumbnails:
frame.save_state()
self.play(
frame.replace, thumbnail,
run_time=2
)
self.wait()
self.play(Restore(frame, run_time=2))
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)
2018-11-29 17:30:34 -08:00
self.play(
ShowCreation(top_line),
FadeInFrom(two_pi_R, IN)
)
self.wait()
self.play(
ShowCreation(side_line),
FadeInFrom(two_R, RIGHT)
)
self.wait()
class CircumferenceOfCylinder(SphereCylinderScene):
def construct(self):
sphere = self.get_sphere()
sphere_ghost = self.get_ghost_surface(sphere)
cylinder = self.get_cylinder()
cylinder_ghost = self.get_ghost_surface(cylinder)
cylinder_ghost.set_stroke(width=0)
radius = self.sphere_config["radius"]
circle = Circle(radius=radius)
circle.set_stroke(YELLOW, 5)
circle.shift(radius * OUT)
height = Line(radius * IN, radius * OUT)
height.shift(radius * LEFT)
height.set_stroke(RED, 5)
self.set_camera_orientation(
phi=70 * DEGREES,
theta=-95 * DEGREES,
)
self.begin_ambient_camera_rotation(0.01)
self.add(sphere_ghost, cylinder_ghost)
self.wait()
self.play(ShowCreation(circle))
self.wait(2)
self.play(ShowCreation(height))
self.wait(5)
class UnfoldCircles(Scene):
CONFIG = {
"circle_style": {
"fill_color": GREY_BROWN,
"fill_opacity": 0,
"stroke_color": GREY_BROWN,
"stroke_width": 2,
},
"radius": 1.0,
"dr": 0.01,
}
def construct(self):
self.show_rectangle_with_formula()
self.add_four_circles()
def show_rectangle_with_formula(self):
TexMobject.CONFIG["background_stroke_width"] = 1
R = self.radius
rect = Rectangle(width=TAU * R, height=2 * R)
rect.set_fill(BLUE_E, 1)
rect.set_stroke(width=0)
p0, p1, p2 = [rect.get_corner(v) for v in (DL, UL, UR)]
h_line = Line(p0, p1)
h_line.set_stroke(RED, 3)
w_line = Line(p1, p2)
w_line.set_stroke(YELLOW, 3)
two_R = TexMobject("2", "R")
two_R.next_to(h_line, LEFT)
two_pi_R = TexMobject("2", "\\pi", "R")
two_pi_R.next_to(w_line, UP)
pre_area_label = TexMobject(
"2\\cdot", "2", "\\pi", "R", "\\cdot R"
)
area_label = TexMobject("4", "\\pi", "R^2")
for label in [area_label, pre_area_label]:
label.next_to(rect.get_center(), UP, SMALL_BUFF)
self.rect_group = VGroup(
rect, h_line, w_line,
two_R, two_pi_R, area_label
)
self.area_label = area_label
self.rect = rect
self.add(rect, h_line, w_line, two_R, two_pi_R)
self.play(
TransformFromCopy(two_R[0], pre_area_label[0]),
TransformFromCopy(two_R[1], pre_area_label[-1]),
TransformFromCopy(two_pi_R, pre_area_label[1:-1]),
)
self.wait()
self.play(
ReplacementTransform(pre_area_label[:2], area_label[:1]),
ReplacementTransform(pre_area_label[2], area_label[1]),
ReplacementTransform(pre_area_label[3:], area_label[2:]),
)
self.wait()
self.play(
self.rect_group.to_corner, UL,
{"buff": MED_SMALL_BUFF}
)
def add_four_circles(self):
rect_group = self.rect_group
radius = self.radius
radii_lines = VGroup(*[
Line(radius * UP, ORIGIN).set_stroke(WHITE, 2)
for x in range(4)
])
2019-02-04 14:54:25 -08:00
radii_lines.arrange_in_grid(buff=1.3)
2018-11-29 17:30:34 -08:00
radii_lines[2:].shift(RIGHT)
radii_lines.next_to(rect_group, DOWN, buff=1.3)
R_labels = VGroup(*[
TexMobject("R").next_to(line, LEFT, SMALL_BUFF)
for line in radii_lines
])
unwrap_factor_tracker = ValueTracker(0)
def get_circle(line):
return always_redraw(
2018-11-29 17:30:34 -08:00
lambda: self.get_unwrapped_circle(
radius=radius, dr=self.dr,
unwrap_factor=unwrap_factor_tracker.get_value(),
center=line.get_top()
)
)
circles = VGroup(*[get_circle(line) for line in radii_lines])
circle_copies = circles.copy()
for mob in circle_copies:
mob.clear_updaters()
self.play(
2019-02-14 12:06:21 -08:00
LaggedStartMap(Write, circle_copies, lag_ratio=0.7),
LaggedStartMap(Write, R_labels),
LaggedStartMap(ShowCreation, radii_lines),
2018-11-29 17:30:34 -08:00
)
self.remove(circle_copies)
self.add(circles, radii_lines, R_labels)
self.wait()
self.play(
radii_lines[2:].shift, 2.9 * RIGHT,
R_labels[2:].shift, 2.9 * RIGHT,
VGroup(
radii_lines[:2], R_labels[:2]
).to_edge, LEFT, {"buff": 1.0}
)
self.play(
unwrap_factor_tracker.set_value, 1,
run_time=2
)
self.wait()
# Move triangles
triangles = circles.copy()
for triangle in triangles:
triangle.clear_updaters()
border = Polygon(*[
triangle.get_corner(vect)
for vect in (DL, UL, DR)
])
border.set_stroke(WHITE, 1)
triangle.add(border)
triangle.generate_target()
rect = self.rect
for i, triangle in enumerate(triangles):
if i % 2 == 1:
triangle.target.rotate(PI)
vect = UP if i < 2 else DOWN
triangle.target.move_to(rect, vect)
self.play(FadeIn(triangles))
self.add(triangles, triangles.copy(), self.area_label)
self.play(MoveToTarget(triangles[0]))
self.wait()
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(MoveToTarget, triangles))
self.wait()
2018-11-29 17:30:34 -08:00
#
def get_unwrapped_circle(self, radius, dr, unwrap_factor=0, center=ORIGIN):
radii = np.arange(0, radius + dr, dr)
rings = VGroup()
for r in radii:
r_factor = 1 + 3 * (r / radius)
alpha = unwrap_factor**r_factor
alpha = np.clip(alpha, 0, 0.9999)
angle = interpolate(TAU, 0, alpha)
length = TAU * r
arc_radius = length / angle
ring = Arc(
start_angle=-90 * DEGREES,
angle=angle,
radius=arc_radius,
**self.circle_style
)
ring.shift(center + (r - arc_radius) * DOWN)
# ring = AnnularSector(
# inner_radius=r1,
# outer_radius=r2,
# angle=TAU,
# start_angle=-TAU / 4,
# **self.circle_style
# )
rings.add(ring)
return rings
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)
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(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(
2019-02-14 12:06:21 -08:00
LaggedStartMap(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
2018-11-29 17:30:34 -08:00
w_brace, h_brace = equation.braces
width_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:
2019-02-07 15:39:45 -08:00
line.add(DashedVMobject(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
)
2018-11-29 17:30:34 -08:00
self.add_fixed_in_frame_mobjects(w_brace, width_label)
self.play(
2018-11-29 17:30:34 -08:00
GrowFromCenter(w_brace),
Write(width_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)
2019-02-04 14:54:25 -08:00
equation.arrange(RIGHT)
equation.to_corner(UR)
brace = Brace(Line(ORIGIN, 0.5 * RIGHT), DOWN)
2018-11-29 17:30:34 -08:00
w_brace = brace.copy().match_width(lhs, stretch=True)
h_brace = brace.copy().rotate(-90 * DEGREES)
h_brace.match_height(lhs, stretch=True)
2018-11-29 17:30:34 -08:00
w_brace.next_to(lhs, DOWN, buff=SMALL_BUFF)
h_brace.next_to(lhs, LEFT, buff=SMALL_BUFF)
2018-11-29 17:30:34 -08:00
braces = VGroup(w_brace, h_brace)
2018-11-29 17:30:34 -08:00
width_label = TextMobject("Width")
height_label = TextMobject("Height")
2018-11-29 17:30:34 -08:00
labels = VGroup(width_label, height_label)
labels.scale(0.75)
2018-11-29 17:30:34 -08:00
width_label.next_to(w_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 = always_redraw(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),
)
2018-11-29 17:30:34 -08:00
# 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")
2018-11-29 17:30:34 -08:00
R_label.next_to(R_line, IN)
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")
2018-11-29 17:30:34 -08:00
d_label.next_to(d_line, IN)
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),
2018-11-29 17:30:34 -08:00
d_label.next_to, to_fade_in, IN,
)
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
2018-11-29 17:30:34 -08:00
s_rect, proj_lines, c_rect = example_group
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(
2018-11-29 17:30:34 -08:00
triangle.copy().rotate(-3 * DEGREES),
TexMobject("\\sim"),
2018-11-29 17:30:34 -08:00
big_triangle.copy().rotate(-3 * DEGREES)
)
2019-02-04 14:54:25 -08:00
equation.arrange(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)
2018-11-29 17:30:34 -08:00
VGroup(equation, eq_d, eq_R).rotate(20 * DEGREES, axis=RIGHT)
for mob in [triangle, big_triangle]:
mob.set_shade_in_3d(True)
mob.set_fill(opacity=0.8)
self.move_camera(
2018-11-29 17:30:34 -08:00
phi=40 * DEGREES,
theta=-85 * DEGREES,
added_anims=[
d_label.next_to, d_line, DOWN, SMALL_BUFF,
2018-11-29 17:30:34 -08:00
d_line.set_stroke, {"width": 1},
FadeOut(proj_lines[0]),
FadeOut(proj_lines[3]),
FadeOut(R_label)
],
run_time=2,
)
2018-11-29 17:30:34 -08:00
point = VectorizedPoint(p0)
point.set_shade_in_3d(True)
self.play(
ReplacementTransform(point, triangle),
Animation(self.camera.phi_tracker)
)
self.add_fixed_in_frame_mobjects(equation, eq_d, eq_R)
self.play(
2018-11-29 17:30:34 -08:00
FadeInFrom(equation[0], 7 * RIGHT + 2.5 * DOWN),
FadeIn(equation[1:]),
FadeInFromDown(eq_d),
FadeInFromDown(eq_R),
2018-11-29 17:30:34 -08:00
Animation(self.camera.phi_tracker)
)
self.wait()
2018-11-29 17:30:34 -08:00
for x in range(2):
self.play(ShowCreationThenDestruction(base))
self.wait()
d_line_copy = d_line.copy().set_stroke(WHITE, 3)
self.play(ShowCreation(d_line_copy))
self.play(FadeOut(d_line_copy))
self.wait(2)
R_label.next_to(R_line, DOWN, SMALL_BUFF)
R_label.shift(0.25 * IN)
self.play(
ReplacementTransform(triangle, big_triangle),
FadeIn(R_label),
2018-11-29 17:30:34 -08:00
Animation(self.camera.phi_tracker)
)
self.wait(3)
self.move_camera(
phi=70 * DEGREES,
theta=-70 * DEGREES,
added_anims=[
2018-11-29 17:30:34 -08:00
big_triangle.set_fill, {"opacity": 0.25},
d_label.next_to, d_line, IN, {"buff": 0.3},
]
)
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)
self.play(
Rotate(
lost_hemisphere, PI,
axis=OUT,
about_point=left_point,
2018-11-29 17:30:34 -08:00
run_time=2,
),
VFadeIn(lost_hemisphere),
FadeOut(self.circle),
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
2018-11-29 17:30:34 -08:00
class JustifyLengthStretchHigherRes(JustifyLengthStretch):
CONFIG = {
"sphere_config": {
"resolution": (2 * 24, 2 * 48)
},
}
class JustifyLengthStretchHighestRes(JustifyLengthStretch):
CONFIG = {
"sphere_config": {
"resolution": (4 * 24, 4 * 48)
},
}
2018-12-26 11:37:07 -08:00
class ProTipNameThings(Scene):
CONFIG = {
"tip_descriptor": "(Deceptively simple) problem solving tip:",
"tip": "Start with names",
}
def construct(self):
words = TextMobject(
self.tip_descriptor,
self.tip,
)
words[1].set_color(YELLOW)
words.to_edge(UP)
self.play(FadeIn(words[0]))
self.wait()
self.play(FadeInFromDown(words[1]))
self.wait()
class WidthScaleLabel(Scene):
def construct(self):
text = TexMobject(
"\\text{Width scale factor} =",
"\\frac{R}{d}"
)
self.play(Write(text))
self.wait()
class HeightSquishLabel(Scene):
def construct(self):
text = TexMobject(
2018-12-26 11:37:07 -08:00
"\\text{Height squish factor} =",
"\\frac{R}{d}"
)
self.play(Write(text))
self.wait()
class TinierAndTinerRectangles(SphereCylinderScene):
2018-11-29 17:30:34 -08:00
CONFIG = {
"n_iterations": 5,
}
def construct(self):
spheres = [
self.get_sphere(
resolution=(12 * (2**n), 24 * (2**n)),
stroke_width=0,
)
2018-11-29 17:30:34 -08:00
for n in range(self.n_iterations)
]
2018-11-29 17:30:34 -08:00
self.set_camera_to_default_position()
2018-11-29 17:30:34 -08:00
self.add(self.get_axes())
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(
2019-02-14 12:06:21 -08:00
LaggedStartMap(Restore, s2)
)
self.remove(s1)
2018-11-29 17:30:34 -08:00
self.wait(5)
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 = {
2018-11-29 17:30:34 -08:00
# As measured from previous scene
"top_phi": 0.5242654422649652,
"low_phi": 0.655081802831207,
2018-11-29 17:30:34 -08:00
"radius": 2,
"R_color": RED,
"d_color": WHITE,
"little_triangle_color": BLUE,
"big_triangle_color": GREY_BROWN,
"alpha_color": WHITE,
"beta_color": WHITE,
}
def construct(self):
2018-11-29 17:30:34 -08:00
self.recreate_cross_section()
self.show_little_triangle()
self.show_tangent_to_radius()
self.tweak_parameter()
self.label_angles()
def recreate_cross_section(self):
axes = Axes(
number_line_config={
"unit_size": 2,
}
)
circle = Circle(
radius=self.radius,
stroke_color=PINK,
stroke_width=2,
)
R_line = self.get_R_line(90 * DEGREES)
R_line.set_color(self.R_color)
R_label = TexMobject("R")
R_label.next_to(R_line, DOWN, SMALL_BUFF)
d_lines = VGroup(*[
self.get_d_line(phi)
for phi in [self.low_phi, self.top_phi]
])
d_line = d_lines[0]
d_line.set_color(self.d_color)
d_label = TexMobject("d")
d_label.next_to(d_line, DOWN, SMALL_BUFF)
proj_lines = VGroup(*[
self.get_R_line(phi)
for phi in [self.top_phi, self.low_phi]
])
proj_lines.set_stroke(YELLOW, 1)
s_rect_line, c_rect_line = [
Line(
*[l.get_end() for l in lines],
stroke_color=YELLOW,
stroke_width=2,
)
for lines in [d_lines, proj_lines]
]
mobjects = [
axes, circle,
R_line, d_line,
R_label, d_label,
proj_lines,
s_rect_line, c_rect_line,
]
self.add(*mobjects)
self.set_variables_as_attrs(*mobjects)
def show_little_triangle(self):
phi = self.low_phi
d_phi = abs(self.low_phi - self.top_phi)
tri_group = self.get_double_triangle_group(phi, d_phi)
lil_tri, big_tri = tri_group
frame = self.camera_frame
frame.save_state()
scale_factor = 0.1
sw_sf = 0.2 # stroke_width scale factor
d_sf = 0.3 # d_label scale factor
hyp = lil_tri.hyp
leg = lil_tri.leg2
leg.rotate(PI)
VGroup(hyp, leg).set_stroke(YELLOW, 1)
hyp_word = TextMobject("Rectangle height $\\rightarrow$")
leg_word = TextMobject("$\\leftarrow$ Projection")
words = VGroup(hyp_word, leg_word)
words.set_height(0.4 * lil_tri.get_height())
words.set_background_stroke(width=0)
hyp_word.next_to(hyp.get_center(), LEFT, buff=0.05)
leg_word.next_to(leg, RIGHT, buff=0.02)
stroke_width_changers = VGroup()
for mob in self.mobjects:
if mob in [self.d_label, frame]:
continue
mob.generate_target()
mob.save_state()
mob.target.set_stroke(
width=sw_sf * mob.get_stroke_width()
)
stroke_width_changers.add(mob)
self.play(
frame.scale, scale_factor,
frame.move_to, lil_tri,
self.d_label.scale, d_sf, {"about_edge": UP},
*map(MoveToTarget, stroke_width_changers)
)
self.play(DrawBorderThenFill(lil_tri, stroke_width=0.5))
self.wait()
self.play(
ShowCreation(hyp),
2019-02-14 12:06:21 -08:00
LaggedStartMap(
2018-11-29 17:30:34 -08:00
DrawBorderThenFill, hyp_word,
stroke_width=0.5,
run_time=1,
),
)
self.wait()
self.play(TransformFromCopy(hyp, leg))
self.play(TransformFromCopy(
hyp_word, leg_word,
path_arc=-PI / 2,
))
self.wait()
self.play(
frame.restore,
self.d_label.scale, 1 / d_sf, {"about_edge": UP},
*map(Restore, stroke_width_changers),
run_time=3
)
self.set_variables_as_attrs(
hyp_word, leg_word, tri_group
)
def show_tangent_to_radius(self):
tri_group = self.tri_group
lil_tri, big_tri = tri_group
lil_hyp = lil_tri.hyp
phi = self.low_phi
circle_point = self.get_circle_point(phi)
tangent = lil_hyp.copy()
tangent.set_stroke(WHITE, 2)
tangent.scale(5 / tangent.get_length())
tangent.move_to(circle_point)
R_line = self.R_line
R_label = self.R_label
d_label = self.d_label
elbow = Elbow(angle=(-phi - PI / 2), width=0.15)
elbow.shift(circle_point)
elbow.set_stroke(WHITE, 1)
self.tangent_elbow = elbow
self.play(GrowFromPoint(tangent, circle_point))
self.wait()
self.play(
Rotate(
R_line, 90 * DEGREES - phi,
about_point=ORIGIN,
),
R_label.next_to, 0.5 * circle_point, DR, {"buff": 0},
d_label.shift, SMALL_BUFF * UL,
)
self.play(ShowCreation(elbow))
self.wait()
self.add(big_tri, d_label, R_line, elbow)
d_label.set_background_stroke(width=0)
self.play(DrawBorderThenFill(big_tri))
self.wait()
self.set_variables_as_attrs(tangent, elbow)
def tweak_parameter(self):
tri_group = self.tri_group
lil_tri = tri_group[0]
d_label = self.d_label
d_line = self.d_line
R_label = self.R_label
R_line = self.R_line
frame = self.camera_frame
to_fade = VGroup(
self.proj_lines,
self.s_rect_line, self.c_rect_line,
self.hyp_word, self.leg_word,
lil_tri.hyp, lil_tri.leg2,
)
rad_tangent = VGroup(
R_line,
self.tangent,
self.elbow,
)
phi_tracker = ValueTracker(self.low_phi)
self.play(
frame.scale, 0.6,
frame.shift, UR,
R_label.scale, 0.6, {"about_edge": UL},
d_label.scale, 0.6,
{"about_point": d_label.get_top() + SMALL_BUFF * DOWN},
*map(FadeOut, to_fade),
)
curr_phi = self.low_phi
d_phi = abs(self.top_phi - self.low_phi)
alt_phis = [
80 * DEGREES,
20 * DEGREES,
50 * DEGREES,
curr_phi
]
for new_phi in alt_phis:
self.add(tri_group, d_label)
self.play(
phi_tracker.set_value, new_phi,
UpdateFromFunc(
tri_group,
lambda tg: tg.become(
self.get_double_triangle_group(
phi_tracker.get_value(),
d_phi
)
)
),
Rotate(
rad_tangent,
-(new_phi - curr_phi),
about_point=ORIGIN,
),
MaintainPositionRelativeTo(R_label, R_line),
UpdateFromFunc(
d_line,
lambda dl: dl.become(
self.get_d_line(phi_tracker.get_value())
),
),
MaintainPositionRelativeTo(d_label, d_line),
run_time=2
)
self.wait()
curr_phi = new_phi
for tri in tri_group:
self.play(Indicate(tri))
self.wait()
self.play(*map(FadeIn, to_fade))
self.remove(phi_tracker)
def label_angles(self):
# Getting pretty hacky here...
tri_group = self.tri_group
lil_tri, big_tri = tri_group
d_label = self.d_label
R_label = self.R_label
frame = self.camera_frame
alpha = self.low_phi
beta = 90 * DEGREES - alpha
circle_point = self.get_circle_point(alpha)
alpha_arc = Arc(
start_angle=90 * DEGREES,
angle=-alpha,
radius=0.2,
stroke_width=2,
)
beta_arc = Arc(
start_angle=PI,
angle=beta,
radius=0.2,
stroke_width=2,
)
beta_arc.shift(circle_point)
alpha_label = TexMobject("\\alpha")
alpha_label.scale(0.5)
alpha_label.set_color(self.alpha_color)
alpha_label.next_to(alpha_arc, UP, buff=SMALL_BUFF)
alpha_label.shift(0.05 * DR)
beta_label = TexMobject("\\beta")
beta_label.scale(0.5)
beta_label.set_color(self.beta_color)
beta_label.next_to(beta_arc, LEFT, buff=SMALL_BUFF)
beta_label.shift(0.07 * DR)
VGroup(alpha_label, beta_label).set_background_stroke(width=0)
elbow = Elbow(width=0.15, angle=-90 * DEGREES)
elbow.shift(big_tri.get_corner(UL))
elbow.set_stroke(width=2)
equation = TexMobject(
"\\alpha", "+", "\\beta", "+",
"90^\\circ", "=", "180^\\circ"
)
equation.scale(0.6)
equation.next_to(frame.get_corner(UR), DL)
movers = VGroup(
alpha_label.deepcopy(), beta_label.deepcopy(),
elbow.copy()
)
indices = [0, 2, 4]
for mover, index in zip(movers, indices):
mover.target = VGroup(equation[index])
# Show equation
self.play(
FadeOut(d_label),
FadeOut(R_label),
ShowCreation(alpha_arc),
ShowCreation(beta_arc),
)
self.wait()
self.play(FadeInFrom(alpha_label, UP))
self.wait()
self.play(FadeInFrom(beta_label, LEFT))
self.wait()
self.play(ShowCreation(elbow))
self.wait()
self.play(
2019-02-14 12:06:21 -08:00
LaggedStartMap(MoveToTarget, movers),
LaggedStartMap(FadeInFromDown, equation[1:4:2])
2018-11-29 17:30:34 -08:00
)
self.wait()
self.play(FadeInFrom(equation[-2:], LEFT))
self.remove(equation, movers)
self.add(equation)
self.wait()
# Zoom in
self.remove(self.tangent_elbow)
stroke_width_changers = VGroup(*[
mob for mob in self.mobjects
if mob not in [
beta_arc, beta_label, frame, equation,
]
])
for mob in stroke_width_changers:
mob.generate_target()
mob.save_state()
mob.target.set_stroke(
width=0.3 * mob.get_stroke_width()
)
equation.set_background_stroke(width=0)
scaled_arcs = VGroup(beta_arc, self.tangent_elbow)
beta_label.set_background_stroke(color=BLACK, width=0.3)
self.play(
ApplyMethod(
VGroup(frame, equation).scale, 0.15,
{"about_point": circle_point + 0.1 * LEFT},
),
ApplyMethod(
beta_label.scale, 0.3,
{"about_point": circle_point},
),
scaled_arcs.set_stroke, {"width": 0.3},
scaled_arcs.scale, 0.3, {"about_point": circle_point},
*map(MoveToTarget, stroke_width_changers)
)
# Show small triangle angles
TexMobject.CONFIG["background_stroke_width"] = 0
words = VGroup(self.hyp_word, self.leg_word)
alpha_arc1 = Arc(
start_angle=90 * DEGREES + beta,
angle=0.95 * alpha,
radius=0.3 * 0.2,
stroke_width=beta_arc.get_stroke_width(),
).shift(circle_point)
alpha_arc2 = Arc(
start_angle=0,
angle=-0.95 * alpha,
radius=0.3 * 0.2,
stroke_width=beta_arc.get_stroke_width(),
).shift(lil_tri.hyp.get_end())
beta_arc1 = Arc(
start_angle=90 * DEGREES,
angle=beta,
radius=0.3 * 0.2,
stroke_width=beta_arc.get_stroke_width(),
).shift(circle_point)
deg90 = TexMobject("90^\\circ")
deg90.set_height(0.8 * beta_label.get_height())
deg90.next_to(self.tangent_elbow, DOWN, buff=0.025)
# deg90.set_background_stroke(width=0)
q_mark = TexMobject("?")
q_mark.set_height(0.5 * beta_label.get_height())
q_mark.next_to(alpha_arc1, LEFT, buff=0.025)
q_mark.shift(0.01 * UP)
alpha_label1 = TexMobject("\\alpha")
alpha_label1.set_height(0.7 * q_mark.get_height())
alpha_label1.move_to(q_mark)
alpha_label2 = alpha_label1.copy()
alpha_label2.next_to(
alpha_arc2, RIGHT, buff=0.01
)
alpha_label2.set_background_stroke(color=BLACK, width=0.3)
beta_label1 = beta_label.copy()
beta_label1.scale(0.7)
beta_label1.set_background_stroke(color=BLACK, width=0.3)
beta_label1.next_to(
beta_arc1, UP, buff=0.01
)
beta_label1.shift(0.01 * LEFT)
self.play(FadeOut(words))
self.play(FadeInFrom(deg90, 0.1 * UP))
self.wait(0.25)
self.play(WiggleOutThenIn(beta_label))
self.wait(0.25)
self.play(
ShowCreation(alpha_arc1),
FadeInFrom(q_mark, 0.1 * RIGHT)
)
self.wait()
self.play(ShowPassingFlash(
self.tangent.copy().scale(0.1).set_stroke(PINK, 0.5)
))
self.wait()
self.play(ReplacementTransform(q_mark, alpha_label1))
self.play(ShowCreationThenFadeAround(
2018-11-29 17:30:34 -08:00
equation,
surrounding_rectangle_config={
"buff": 0.015,
"stroke_width": 0.5,
},
))
self.wait()
self.play(
ShowCreation(alpha_arc2),
FadeIn(alpha_label2),
)
self.play(
ShowCreation(beta_arc1),
FadeIn(beta_label1),
)
self.wait()
#
def get_double_triangle_group(self, phi, d_phi):
p0 = self.get_circle_point(phi)
p1 = self.get_circle_point(phi - d_phi)
p2 = np.array(p1)
p2[0] = p0[0]
little_triangle = Polygon(
p0, p1, p2,
stroke_width=0,
fill_color=self.little_triangle_color,
fill_opacity=1,
)
big_triangle = Polygon(
p0, ORIGIN, p0 - p0[0] * RIGHT,
stroke_width=0,
fill_color=self.big_triangle_color,
fill_opacity=1
)
result = VGroup(little_triangle, big_triangle)
for tri in result:
p0, p1, p2 = tri.get_anchors()[:3]
tri.hyp = Line(p0, p1)
tri.leg1 = Line(p1, p2)
tri.leg2 = Line(p2, p0)
tri.side_lines = VGroup(
tri.hyp, tri.leg1, tri.leg2
)
tri.side_lines.set_stroke(WHITE, 1)
result.set_stroke(width=0)
return result
def get_R_line(self, phi):
y = self.radius * np.cos(phi)
x = self.radius
return Line(ORIGIN, x * RIGHT).shift(y * UP)
def get_d_line(self, phi):
end = self.get_circle_point(phi)
start = np.array(end)
start[0] = 0
return Line(start, end)
def get_circle_point(self, phi):
return rotate_vector(self.radius * UP, -phi)
2018-12-26 11:37:07 -08:00
class ProTipTangentRadii(ProTipNameThings):
CONFIG = {
"tip_descriptor": "Pro tip:",
"tip": "A circle's tangent is perpendicular to its radius",
}
class ProTipTweakParameters(ProTipNameThings):
CONFIG = {
"tip_descriptor": "Pro tip:",
"tip": "Tweak parameters $\\rightarrow$ make",
}
class DrawSquareThenFade(Scene):
def construct(self):
square = Square(color=YELLOW, stroke_width=5)
self.play(ShowCreation(square))
self.play(FadeOut(square))
2018-11-29 17:30:34 -08:00
class WhyAreWeDoingThis(TeacherStudentsScene):
def construct(self):
self.student_says(
"Hang on, what \\\\ are we doing?",
student_index=2,
bubble_kwargs={"direction": LEFT},
target_mode="hesitant"
)
self.change_student_modes(
"maybe", "pondering", "hesitant",
added_anims=[self.teacher.change, "tease"]
)
self.wait(3)
self.play(
RemovePiCreatureBubble(self.students[2]),
self.teacher.change, "raise_right_hand",
self.change_student_modes(*2 * ["pondering"])
)
self.look_at(self.screen)
self.wait(2)
class SameEffectAsRotating(Scene):
2018-12-26 11:37:07 -08:00
CONFIG = {
"rectangle_config": {
"height": 2,
"width": 1,
"stroke_width": 0,
"fill_color": YELLOW,
"fill_opacity": 1,
"background_stroke_width": 2,
"background_stroke_color": BLACK,
},
"x_stretch": 2,
"y_stretch": 0.5,
}
2018-11-29 17:30:34 -08:00
def construct(self):
2018-12-26 11:37:07 -08:00
rect1 = Rectangle(**self.rectangle_config)
rect2 = rect1.copy()
rect2.stretch(self.x_stretch, 0)
rect2.stretch(self.y_stretch, 1)
rotated_rect1 = rect1.copy()
rotated_rect1.rotate(-90 * DEGREES)
2018-11-29 17:30:34 -08:00
arrow = Arrow(ORIGIN, RIGHT, buff=0, color=WHITE)
group = VGroup(rect1, arrow, rect2)
2019-02-04 14:54:25 -08:00
group.arrange(RIGHT)
2018-11-29 17:30:34 -08:00
group.center()
moving_rect = rect1.copy()
low_brace = always_redraw(
2018-12-26 11:37:07 -08:00
lambda: Brace(
moving_rect, DOWN, buff=SMALL_BUFF,
min_num_quads=2,
)
2018-11-29 17:30:34 -08:00
)
right_brace = always_redraw(
2018-12-26 11:37:07 -08:00
lambda: Brace(
moving_rect, RIGHT, buff=SMALL_BUFF,
min_num_quads=2,
)
2018-11-29 17:30:34 -08:00
)
times_R_over_d = TexMobject("\\times \\frac{R}{d}")
times_d_over_R = TexMobject("\\times \\frac{d}{R}")
times_R_over_d.add_updater(
lambda m: m.next_to(low_brace, DOWN, SMALL_BUFF)
)
times_d_over_R.add_updater(
lambda m: m.next_to(right_brace, RIGHT, SMALL_BUFF)
)
self.add(rect1, arrow)
self.play(moving_rect.move_to, rect2)
self.add(low_brace)
self.play(
moving_rect.match_width, rect2, {"stretch": True},
FadeIn(times_R_over_d),
)
self.add(right_brace)
self.play(
moving_rect.match_height, rect2, {"stretch": True},
FadeIn(times_d_over_R),
)
self.wait()
2018-12-26 11:37:07 -08:00
rotated_rect1.move_to(moving_rect)
2018-11-29 17:30:34 -08:00
self.play(TransformFromCopy(
2018-12-26 11:37:07 -08:00
rect1, rotated_rect1,
2018-11-29 17:30:34 -08:00
path_arc=-90 * DEGREES,
run_time=2
))
2018-12-26 11:37:07 -08:00
class NotSameEffectAsRotating(SameEffectAsRotating):
CONFIG = {
"rectangle_config": {
"width": 1.5,
"height": 1.5,
}
}
class ShowParameterization(Scene):
def construct(self):
u_color = PINK
v_color = GREEN
tex_kwargs = {
"tex_to_color_map": {
"u": u_color,
"v": v_color,
}
}
vector = Matrix(
[
["\\text{cos}(u)\\text{sin}(v)"],
["\\text{sin}(u)\\text{sin}(v)"],
["\\text{cos}(v)"]
],
element_to_mobject_config=tex_kwargs,
element_alignment_corner=DOWN,
)
vector.to_edge(UP)
ranges = VGroup(
TexMobject("0 \\le u \\le 2\\pi", **tex_kwargs),
TexMobject("0 \\le v \\le \\pi", **tex_kwargs),
TextMobject(
"Sample $u$ and $v$ with \\\\ the same density",
tex_to_color_map={
"$u$": u_color,
"$v$": v_color,
}
)
)
2019-02-04 14:54:25 -08:00
ranges.arrange(DOWN)
2018-12-26 11:37:07 -08:00
ranges.next_to(vector, DOWN)
self.add(vector)
self.add(ranges)
class RdLabels(Scene):
def construct(self):
rect = Rectangle(height=1, width=0.5)
cR = TexMobject("cR")
cR.next_to(rect, LEFT, SMALL_BUFF)
cd = TexMobject("cd")
cd.next_to(rect, DOWN, SMALL_BUFF)
labels = VGroup(cd, cR)
for label in labels:
label[1].set_color(BLUE)
self.play(FadeInFromDown(label))
2018-11-29 17:30:34 -08:00
class RotateAllPiecesWithExpansion(ShowProjection):
CONFIG = {
"sphere_config": {
"radius": 1.5,
},
"with_expansion": True
}
def construct(self):
self.setup_shapes()
self.rotate_all_pieces()
def rotate_all_pieces(self):
sphere = self.sphere
cylinder = self.cylinder
ghost_sphere = self.ghost_sphere
ghost_sphere.scale(0.99)
# Shuffle sphere and cylinder same way
random.seed(0)
random.shuffle(sphere.submobjects)
random.seed(0)
random.shuffle(cylinder.submobjects)
sphere_target = VGroup()
for piece in sphere:
p0, p1, p2, p3 = piece.get_anchors()[:4]
piece.set_points_as_corners([
p3, p0, p1, p2, p3
])
piece.generate_target()
sphere_target.add(piece.target)
piece.target.move_to(
(1 + random.random()) * piece.get_center()
)
self.add(ghost_sphere, sphere)
self.wait()
if self.with_expansion:
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(
2018-11-29 17:30:34 -08:00
MoveToTarget, sphere
))
self.wait()
self.play(*[
Rotate(piece, 90 * DEGREES, axis=piece.get_center())
for piece in sphere
])
self.wait()
self.play(Transform(sphere, cylinder, run_time=2))
self.wait(5)
class RotateAllPiecesWithoutExpansion(RotateAllPiecesWithExpansion):
CONFIG = {
"with_expansion": False,
}
class ThinkingCritically(PiCreatureScene):
def construct(self):
randy = self.pi_creature
self.play(randy.change, "pondering")
self.wait()
self.play(
randy.change, "hesitant", 2 * UP,
)
self.wait()
self.play(randy.change, "sassy")
self.wait()
self.play(randy.change, "angry")
self.wait(4)
class WriteNotEquals(Scene):
def construct(self):
symbol = TexMobject("\\ne")
symbol.scale(2)
symbol.set_background_stroke(width=0)
self.play(Write(symbol))
self.wait()
class RectangulatedSphere(SphereCylinderScene):
CONFIG = {
"sphere_config": {
"resolution": (10, 20)
},
"uniform_color": False,
"wait_time": 10,
}
def construct(self):
sphere = self.get_sphere()
if self.uniform_color:
sphere.set_stroke(BLUE_E, width=0.5)
sphere.set_fill(BLUE_E)
self.set_camera_to_default_position()
self.begin_ambient_camera_rotation(0.05)
self.add(sphere)
self.wait(self.wait_time)
class SmoothSphere(RectangulatedSphere):
CONFIG = {
"sphere_config": {
"resolution": (200, 400),
},
"uniform_color": True,
"wait_time": 0,
}
class SequenceOfSpheres(SphereCylinderScene):
def construct(self):
n_shapes = 4
spheres, cylinders = groups = VGroup(*[
VGroup(*[
func(resolution=(n, 2 * n))
for k in range(1, n_shapes + 1)
for n in [3 * (2**k)]
])
for func in [self.get_sphere, self.get_cylinder]
])
groups.scale(0.5)
for group in groups:
for shape in group:
for piece in shape:
piece.make_jagged()
shape.set_stroke(width=0)
for group in groups:
group.add(self.get_oriented_tex("?").scale(2))
2019-02-04 14:54:25 -08:00
group.arrange(RIGHT, buff=LARGE_BUFF)
groups.arrange(IN, buff=1.5)
2018-11-29 17:30:34 -08:00
all_equals = VGroup()
for sphere, cylinder in zip(spheres, cylinders):
equals = self.get_oriented_tex("=")
equals.scale(1.5)
equals.rotate(90 * DEGREES, UP)
equals.move_to(interpolate(
sphere.get_nadir(), cylinder.get_zenith(), 0.5
))
all_equals.add(equals)
all_equals.remove(all_equals[-1])
arrow_groups = VGroup()
for group in groups:
arrow_group = VGroup()
for m1, m2 in zip(group, group[1:]):
arrow = self.get_oriented_tex("\\rightarrow")
arrow.move_to(interpolate(
m1.get_right(), m2.get_left(), 0.5
))
arrow_group.add(arrow)
arrow_groups.add(arrow_group)
q_marks = VGroup(*[
group[-1]
for group in groups
])
final_arrows = VGroup(
arrow_groups[0][-1],
arrow_groups[1][-1],
)
for arrow in final_arrows:
dots = self.get_oriented_tex("\\dots")
dots.next_to(arrow, RIGHT, SMALL_BUFF)
arrow.add(dots)
q_marks.shift(MED_LARGE_BUFF * RIGHT)
tilted_final_arrows = VGroup(
final_arrows[0].copy().rotate(
-45 * DEGREES, axis=DOWN
).shift(0.75 * IN),
final_arrows[1].copy().rotate(
45 * DEGREES, axis=DOWN
).shift(0.75 * OUT),
)
final_q_mark = q_marks[0].copy()
final_q_mark.move_to(q_marks)
self.set_camera_orientation(
phi=80 * DEGREES,
theta=-90 * DEGREES,
)
for i in range(n_shapes):
anims = [
FadeInFrom(spheres[i], LEFT),
FadeInFrom(cylinders[i], LEFT),
]
if i > 0:
anims += [
Write(arrow_group[i - 1])
for arrow_group in arrow_groups
]
self.play(*anims, run_time=1)
self.play(GrowFromCenter(all_equals[i]))
self.play(
FadeInFrom(q_marks, LEFT),
Write(final_arrows)
)
self.wait()
self.play(
Transform(final_arrows, tilted_final_arrows),
Transform(q_marks, VGroup(final_q_mark)),
)
self.wait()
def get_oriented_tex(self, tex):
result = TexMobject(tex)
result.rotate(90 * DEGREES, RIGHT)
return result
class WhatIsSurfaceArea(SpecialThreeDScene):
2018-12-26 11:37:07 -08:00
CONFIG = {
"change_power": True,
}
2018-11-29 17:30:34 -08:00
def construct(self):
title = TextMobject("What is surface area?")
title.scale(1.5)
title.to_edge(UP)
title.shift(0.035 * RIGHT)
self.add_fixed_in_frame_mobjects(title)
power_tracker = ValueTracker(1)
surface = always_redraw(
2018-11-29 17:30:34 -08:00
lambda: self.get_surface(
radius=3,
amplitude=1,
power=power_tracker.get_value()
)
)
pieces = surface.copy()
pieces.clear_updaters()
random.shuffle(pieces.submobjects)
self.set_camera_to_default_position()
self.begin_ambient_camera_rotation()
2018-12-26 11:37:07 -08:00
# self.add(self.get_axes())
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(
2018-11-29 17:30:34 -08:00
DrawBorderThenFill, pieces,
lag_ratio=0.2,
))
self.remove(pieces)
self.add(surface)
2018-12-26 11:37:07 -08:00
if self.change_power:
self.play(
power_tracker.set_value, 5,
run_time=2
)
self.play(
power_tracker.set_value, 1,
run_time=2
)
2018-11-29 17:30:34 -08:00
self.wait(2)
def get_surface(self, radius, amplitude, power):
def alt_pow(x, y):
return np.sign(x) * (np.abs(x) ** y)
return ParametricSurface(
lambda u, v: radius * np.array([
v * np.cos(TAU * u),
v * np.sin(TAU * u),
0,
]) + amplitude * np.array([
0,
0,
(v**2) * alt_pow(np.sin(5 * TAU * u), power),
]),
resolution=(100, 20),
v_min=0.01
)
2018-12-26 11:37:07 -08:00
class AltWhatIsSurfaceArea(WhatIsSurfaceArea):
CONFIG = {
"change_power": False,
}
def get_surface(self, radius, amplitude, power):
return ParametricSurface(
lambda u, v: radius * (1 - 0.8 * (v**2) ** power) * np.array([
np.cos(TAU * u) * (1 + 0.5 * v * np.sin(5 * TAU * u)),
np.sin(TAU * u) * (1 + 0.5 * v * np.sin(5 * TAU * u)),
v,
]),
v_min=-1,
v_max=1,
resolution=(100, 25),
)
class EoCWrapper(Scene):
def construct(self):
title = TextMobject("Essence of calculus")
title.scale(1.5)
title.to_edge(UP)
rect = ScreenRectangle(height=6)
rect.next_to(title, DOWN)
self.add(title)
self.play(ShowCreation(rect))
self.wait()
class RoleOfCalculus(SpecialThreeDScene):
CONFIG = {
"camera_config": {
"light_source_start_point": [-4, 5, 7],
}
}
def construct(self):
calc = TexMobject("\\int", "\\int")
calc.space_out_submobjects(0.4)
calc.scale(2)
arrow = Vector(2 * RIGHT, color=WHITE)
sphere = self.get_sphere()
sphere.rotate(70 * DEGREES, axis=LEFT)
group = VGroup(calc, arrow, sphere)
2019-02-04 14:54:25 -08:00
group.arrange(RIGHT)
2018-12-26 11:37:07 -08:00
group.shift(0.5 * RIGHT)
cross = Cross(group[:2], stroke_width=10)
# arrow2 = arrow.copy()
self.add(calc, arrow)
self.play(Write(sphere))
self.wait()
self.play(ShowCreation(cross))
self.wait()
self.play(
sphere.next_to, ORIGIN, LEFT, 1.0,
arrow.move_to, ORIGIN, LEFT,
calc.next_to, ORIGIN, RIGHT, 2.25,
FadeOut(cross),
path_arc=PI,
run_time=2,
)
self.wait()
2018-11-29 17:30:34 -08:00
class UnwrappedCircleLogic(UnfoldCircles):
2018-12-26 11:37:07 -08:00
CONFIG = {
"radius": 1.25,
"dr": 0.01,
}
2018-11-29 17:30:34 -08:00
def construct(self):
2018-12-26 11:37:07 -08:00
radius = self.radius
dr = self.dr
2018-11-29 17:30:34 -08:00
TexMobject.CONFIG["background_stroke_width"] = 2
unwrap_factor_tracker = ValueTracker(0)
center_tracker = VectorizedPoint()
highligt_prop_tracker = ValueTracker(0.5)
def get_highlight_prop():
return highligt_prop_tracker.get_value()
def get_r():
return radius * get_highlight_prop()
center_tracker.move_to(4.5 * LEFT)
def get_unwrapped_circle():
result = self.get_unwrapped_circle(
radius=radius, dr=dr,
unwrap_factor=unwrap_factor_tracker.get_value(),
center=center_tracker.get_center()
)
self.get_submob_from_prop(
result, get_highlight_prop()
2018-12-26 11:37:07 -08:00
).set_stroke(YELLOW, 2)
2018-11-29 17:30:34 -08:00
return result
unwrapped_circle = always_redraw(get_unwrapped_circle)
2018-11-29 17:30:34 -08:00
circle = unwrapped_circle.copy()
circle.clear_updaters()
R_line = Line(circle.get_center(), circle.get_bottom())
R_line.set_stroke(WHITE, 2)
R_label = TexMobject("R")
R_label.next_to(R_line, LEFT)
circle_group = VGroup(circle, R_line, R_label)
tri_R_line = always_redraw(
2018-11-29 17:30:34 -08:00
lambda: Line(
ORIGIN, radius * DOWN
).shift(center_tracker.get_center())
)
# Unwrap
self.play(FadeInFromDown(circle_group))
self.add(circle_group, unwrapped_circle, tri_R_line, R_label)
circle_group.set_stroke(opacity=0.5)
self.play(
unwrap_factor_tracker.set_value, 1,
run_time=2
)
self.play(
center_tracker.move_to,
circle.get_right() + (radius + MED_SMALL_BUFF) * RIGHT,
circle_group.set_stroke, {"opacity": 1},
)
self.wait()
# Change radius
r_line = always_redraw(
2018-11-29 17:30:34 -08:00
lambda: Line(
ORIGIN, get_r() * DOWN,
stroke_width=2,
stroke_color=WHITE,
).shift(circle.get_center())
)
r_label = TexMobject("r")
r_label.add_updater(
lambda m: m.next_to(r_line, LEFT, SMALL_BUFF)
)
two_pi_r_label = TexMobject("2\\pi r")
two_pi_r_label.add_updater(
lambda m: m.next_to(
self.get_submob_from_prop(
unwrapped_circle,
get_highlight_prop(),
), DOWN, SMALL_BUFF
)
)
circle.add_updater(
lambda m: m.match_style(unwrapped_circle)
)
self.play(
ReplacementTransform(R_line, r_line),
ReplacementTransform(R_label, r_label),
FadeInFromDown(
two_pi_r_label.copy().clear_updaters(),
remover=True
)
)
self.add(two_pi_r_label)
for prop in [0.2, 0.8, 0.5]:
self.play(
highligt_prop_tracker.set_value, prop,
run_time=2
)
# Show line
line = Line(*[
unwrapped_circle.get_corner(vect)
for vect in (UL, DR)
])
line.set_color(PINK)
line.set_fill(BLACK, 1)
line_word = TextMobject("Line")
line_word.next_to(ORIGIN, UP, SMALL_BUFF)
line_word.rotate(line.get_angle(), about_point=ORIGIN)
line_word.shift(line.get_center())
curve = line.copy()
curve.points[1] = unwrapped_circle.get_corner(DL)
not_line = TextMobject("Not line")
not_line.rotate(line.get_angle() / 2)
not_line.move_to(line_word)
not_line.shift(0.3 * DOWN)
self.play(
ShowCreation(line),
Write(line_word),
)
self.wait()
self.play(highligt_prop_tracker.set_value, 1)
self.wait()
# Bend
line.save_state()
line_word.save_state()
self.play(
Transform(line, curve),
Transform(line_word, not_line),
)
self.wait()
self.play(
Restore(line),
Restore(line_word),
# FadeIn(two_pi_r_label),
)
self.wait()
def get_submob_from_prop(self, mob, prop):
n = len(mob.submobjects)
return mob[min(int(prop * n), n - 1)]
class AskAboutDirectConnection(TeacherStudentsScene, SpecialThreeDScene):
CONFIG = {
"camera_config": {
"light_source_start_point": [-4, 5, 7],
}
}
def construct(self):
sphere = Sphere()
cylinder = Cylinder()
for mob in sphere, cylinder:
mob.rotate(70 * DEGREES, LEFT)
formula = TexMobject("4\\pi R^2")
formula.set_color(BLUE)
circle = Circle()
circle.set_stroke(width=0)
circle.set_fill(GREY_BROWN, 1)
area_label = TexMobject("\\pi R^2", background_stroke_width=0)
area_label.scale(1.5)
circle.add(area_label)
group = VGroup(
sphere, cylinder, formula, circle
)
for mob in group:
mob.set_height(1.5)
formula.scale(0.5)
2019-02-04 14:54:25 -08:00
group.arrange(RIGHT, buff=1.5)
2018-11-29 17:30:34 -08:00
group.to_edge(UP, buff=2)
group[1:3].to_edge(UP)
arrows = VGroup()
for m1, m2 in zip(group, group[1:]):
arrow = Arrow(
m1.get_center(), m2.get_center(),
buff=1,
color=WHITE
)
arrows.add(arrow)
direct_arrow = Arrow(
sphere, circle, color=WHITE
)
q_marks = TexMobject(*"???")
q_marks.space_out_submobjects(1.5)
q_marks.scale(1.5)
q_marks.next_to(direct_arrow, DOWN)
self.play(
self.teacher.change, "raise_right_hand",
self.get_student_changes(
*3 * ["pondering"],
look_at_arg=group,
),
2019-02-14 12:06:21 -08:00
LaggedStartMap(FadeInFromDown, group),
LaggedStartMap(GrowArrow, arrows)
2018-11-29 17:30:34 -08:00
)
self.wait()
self.play(
self.teacher.change, "pondering",
self.students[2].change, "raise_right_hand",
GrowArrow(direct_arrow),
2019-02-14 12:06:21 -08:00
LaggedStartMap(
2018-11-29 17:30:34 -08:00
FadeInFrom, q_marks,
lambda m: (m, UP),
lag_ratio=0.8,
run_time=1.5,
)
)
self.change_student_modes(
"erm", "sassy", "raise_right_hand",
)
self.wait(2)
self.look_at(group)
self.wait(2)
class ExercisesGiveLearning(MovingCameraScene):
def construct(self):
bulb = Lightbulb()
arrow1 = Arrow(ORIGIN, RIGHT, buff=0)
lectures = TextMobject("Lectures")
exercises = TextMobject("Exercises")
frame = self.camera_frame
frame.scale(0.7)
bulb.next_to(arrow1, RIGHT)
for word in lectures, exercises:
word.next_to(arrow1, LEFT)
cross = Cross(lectures)
# Knock down lectures
self.add(lectures)
self.play(GrowArrow(arrow1))
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(DrawBorderThenFill, bulb))
2018-11-29 17:30:34 -08:00
self.play(ShowCreation(cross))
self.play(
VGroup(lectures, cross).shift, DOWN,
FadeInFrom(exercises, UP)
)
self.wait()
# Show self
arrow2 = arrow1.copy()
arrow2.next_to(lectures, LEFT)
logo = Logo()
logo.set_height(1)
logo.next_to(arrow2, LEFT)
pupil_copy = logo.pupil.copy()
self.add(logo, pupil_copy)
self.play(
frame.shift, 1.5 * LEFT,
Write(logo, run_time=1.5)
)
self.remove(pupil_copy)
self.play(
GrowArrow(arrow2),
FadeOut(cross)
)
self.wait()
self.play(
VGroup(logo, arrow2).next_to,
exercises, LEFT
)
self.wait()
class NobodyLikesHomework(TeacherStudentsScene):
def construct(self):
self.change_student_modes(
"angry", "pleading", "angry",
added_anims=[self.teacher.change, "guilty"]
)
self.wait()
self.change_all_student_modes(
"tired", look_at_arg=8 * RIGHT + 4 * DOWN,
added_anims=[self.teacher.change, "tease"]
)
self.wait(2)
2018-12-26 11:37:07 -08:00
class ChallengeMode(Scene):
def construct(self):
words = TextMobject("Challenge mode: Predict the proof")
words.scale(1.5)
words.to_edge(UP)
self.play(Write(words))
self.wait()
2018-11-29 17:30:34 -08:00
class SecondProof(SpecialThreeDScene):
CONFIG = {
"sphere_config": {
2018-11-29 22:44:08 -08:00
"resolution": (30, 30),
},
"n_random_subsets": 12,
2018-11-29 17:30:34 -08:00
}
def construct(self):
self.setup_shapes()
self.divide_into_rings()
self.show_shadows()
self.correspond_to_every_other_ring()
self.cut_cross_section()
self.show_theta()
self.enumerate_rings()
2018-11-29 22:44:08 -08:00
self.ask_about_ring_circumference()
2018-11-29 17:30:34 -08:00
self.ask_about_shadow_area()
self.ask_about_2_to_1_correspondance()
2018-11-29 22:44:08 -08:00
self.show_all_shadow_rings()
2018-11-29 17:30:34 -08:00
self.ask_about_global_correspondance()
def setup_shapes(self):
sphere = self.get_sphere()
sphere.set_stroke(WHITE, width=0.25)
self.add(sphere)
self.sphere = sphere
2018-11-29 22:44:08 -08:00
u_values, v_values = sphere.get_u_values_and_v_values()
rings = VGroup(*[VGroup() for u in u_values])
for piece in sphere:
rings[piece.u_index].add(piece.copy())
self.set_ring_colors(rings)
2018-11-29 17:30:34 -08:00
self.rings = rings
self.axes = self.get_axes()
self.add(self.axes)
self.set_camera_to_default_position()
self.begin_ambient_camera_rotation()
def divide_into_rings(self):
rings = self.rings
2018-11-29 22:44:08 -08:00
self.play(FadeIn(rings), FadeOut(self.sphere))
2018-11-29 17:30:34 -08:00
self.play(
rings.space_out_submobjects, 1.5,
rate_func=there_and_back_with_pause,
run_time=3
)
self.wait(2)
2018-11-29 22:44:08 -08:00
rings.save_state()
2018-11-29 17:30:34 -08:00
def show_shadows(self):
rings = self.rings
2018-11-29 22:44:08 -08:00
north_rings = rings[:len(rings) // 2]
2018-11-29 17:30:34 -08:00
ghost_rings = rings.copy()
ghost_rings.set_fill(opacity=0.0)
2018-12-26 11:37:07 -08:00
ghost_rings.set_stroke(WHITE, width=0.5, opacity=0.2)
2018-11-29 17:30:34 -08:00
north_rings.submobjects.reverse()
shadows = self.get_shadow(north_rings)
for piece in shadows.family_members_with_points():
piece.set_stroke(
piece.get_fill_color(),
width=0.5,
)
for shadow in shadows:
shadow.save_state()
shadows.become(north_rings)
self.add(ghost_rings)
self.play(FadeOut(rings), Animation(shadows))
2019-02-14 12:06:21 -08:00
self.play(LaggedStartMap(Restore, shadows))
2018-11-29 17:30:34 -08:00
self.wait()
self.move_camera(phi=40 * DEGREES)
self.wait(3)
# Show circle
radius = self.sphere_config["radius"]
radial_line = Line(ORIGIN, radius * RIGHT)
radial_line.set_stroke(RED)
R_label = TexMobject("R")
R_label.set_background_stroke(width=1)
R_label.next_to(radial_line, DOWN)
self.play(
FadeInFromDown(R_label),
ShowCreation(radial_line)
)
self.play(Rotating(
radial_line, angle=TAU,
about_point=ORIGIN,
rate_func=smooth,
run_time=3,
))
self.wait()
self.set_variables_as_attrs(
shadows, ghost_rings,
radial_line, R_label
)
def correspond_to_every_other_ring(self):
rings = self.rings
shadows = self.shadows
2018-11-29 22:44:08 -08:00
shadows.submobjects.reverse()
2018-11-29 17:30:34 -08:00
2018-12-26 11:37:07 -08:00
rings.restore()
self.set_ring_colors(rings)
2018-11-29 17:30:34 -08:00
every_other_ring = rings[1::2]
self.move_camera(
phi=70 * DEGREES,
theta=-135 * DEGREES,
added_anims=[
FadeOut(self.R_label),
FadeOut(self.radial_line),
],
run_time=2,
)
2018-11-29 22:44:08 -08:00
shadows_copy = shadows.copy()
shadows.fade(1)
2018-11-29 17:30:34 -08:00
self.play(
2018-11-29 22:44:08 -08:00
ReplacementTransform(
shadows_copy, every_other_ring
),
2018-12-26 11:37:07 -08:00
FadeOut(self.ghost_rings),
2018-11-29 17:30:34 -08:00
run_time=2,
)
self.wait(5)
self.every_other_ring = every_other_ring
def cut_cross_section(self):
shadows = self.shadows
every_other_ring = self.every_other_ring
rings = self.rings
back_half = self.get_hemisphere(rings, UP)
front_half = self.get_hemisphere(rings, DOWN)
2018-11-29 22:44:08 -08:00
front_half_ghost = front_half.copy()
2018-12-26 11:37:07 -08:00
front_half_ghost.set_fill(opacity=0.2)
front_half_ghost.set_stroke(opacity=0)
2018-11-29 17:30:34 -08:00
# shaded_back_half = back_half.copy()
2018-11-29 22:44:08 -08:00
# for piece in shaded_back_half.family_members_with_points():
# piece.points = piece.points[::-1]
# shaded_back_half.scale(0.999)
# shaded_back_half.set_fill(opacity=0.5)
2018-11-29 17:30:34 -08:00
radius = self.sphere_config["radius"]
circle = Circle(radius=radius)
circle.set_stroke(PINK, 2)
circle.rotate(90 * DEGREES, RIGHT)
every_other_ring_copy = every_other_ring.copy()
self.add(every_other_ring_copy)
2018-12-26 11:37:07 -08:00
self.remove(every_other_ring)
2018-11-29 17:30:34 -08:00
rings.set_fill(opacity=0.8)
2018-12-26 11:37:07 -08:00
rings.set_stroke(opacity=0.6)
2018-11-29 17:30:34 -08:00
self.play(
2018-12-26 11:37:07 -08:00
FadeIn(back_half),
FadeIn(front_half_ghost),
2018-11-29 17:30:34 -08:00
FadeIn(circle),
2018-12-26 11:37:07 -08:00
FadeOut(shadows),
FadeOut(every_other_ring_copy),
2018-11-29 17:30:34 -08:00
)
self.wait()
2018-11-29 22:44:08 -08:00
self.set_variables_as_attrs(
back_half, front_half,
front_half_ghost,
slice_circle=circle
)
2018-11-29 17:30:34 -08:00
def show_theta(self):
theta_tracker = ValueTracker(0)
get_theta = theta_tracker.get_value
theta_group = always_redraw(
2018-11-29 17:30:34 -08:00
lambda: self.get_theta_group(get_theta())
)
theta_mob_opacity_tracker = ValueTracker(0)
get_theta_mob_opacity = theta_mob_opacity_tracker.get_value
theta_mob = theta_group[-1]
theta_mob.add_updater(
lambda m: m.set_fill(opacity=get_theta_mob_opacity())
)
theta_mob.add_updater(
lambda m: m.set_background_stroke(
width=get_theta_mob_opacity()
)
)
lit_ring = always_redraw(
2018-11-29 17:30:34 -08:00
lambda: self.get_ring_from_theta(
self.rings, get_theta()
).copy().set_color(YELLOW)
)
self.stop_ambient_camera_rotation()
self.move_camera(theta=-60 * DEGREES)
self.add(theta_group, lit_ring)
2018-11-29 22:44:08 -08:00
n_rings = len(self.rings) - 1
lit_ring_index = int((30 / 180) * n_rings)
angle = PI * lit_ring_index / n_rings
for alpha in [angle, 0, PI, angle]:
2018-11-29 17:30:34 -08:00
self.play(
2018-11-29 22:44:08 -08:00
theta_tracker.set_value, alpha,
2018-11-29 17:30:34 -08:00
theta_mob_opacity_tracker.set_value, 1,
Animation(self.camera.phi_tracker),
run_time=2,
)
self.wait()
# Label d-theta
radius = self.sphere_config["radius"]
d_theta = PI / len(self.rings)
alt_theta = get_theta() + d_theta
alt_theta_group = self.get_theta_group(alt_theta)
alt_R_line = alt_theta_group[1]
# d_theta_arc = Arc(
# start_angle=get_theta(),
# angle=d_theta,
# radius=theta_group[0].radius,
# stroke_color=PINK,
# stroke_width=3,
# )
# d_theta_arc.rotate(90 * DEGREES, axis=RIGHT, about_point=ORIGIN)
brace = Brace(Line(ORIGIN, radius * d_theta * RIGHT), UP)
brace.rotate(90 * DEGREES, RIGHT)
brace.next_to(self.sphere, OUT, buff=0)
brace.add_to_back(brace.copy().set_stroke(BLACK, 3))
brace.rotate(
get_theta() + d_theta / 2,
axis=UP,
about_point=ORIGIN,
)
brace_label = TexMobject("R\\,d\\theta")
brace_label.rotate(90 * DEGREES, RIGHT)
brace_label.next_to(brace, OUT + RIGHT, buff=0)
radial_line = self.radial_line
R_label = self.R_label
R_label.rotate(90 * DEGREES, RIGHT)
R_label.next_to(radial_line, IN, SMALL_BUFF)
self.play(
TransformFromCopy(theta_group[1], alt_R_line),
GrowFromCenter(brace),
2018-12-26 11:37:07 -08:00
Animation(self.camera.phi_tracker),
2018-11-29 17:30:34 -08:00
)
2018-12-26 11:37:07 -08:00
self.wait()
2018-11-29 17:30:34 -08:00
self.move_camera(
phi=90 * DEGREES,
theta=-90 * DEGREES,
)
self.wait()
2018-12-26 11:37:07 -08:00
self.play(
FadeInFrom(brace_label, IN),
)
2018-11-29 17:30:34 -08:00
self.play(
ShowCreation(radial_line),
FadeIn(R_label),
)
self.wait()
self.move_camera(
phi=70 * DEGREES,
theta=-70 * DEGREES,
)
2018-12-26 11:37:07 -08:00
self.wait(3)
2018-11-29 17:30:34 -08:00
2018-11-29 22:44:08 -08:00
self.set_variables_as_attrs(
theta_tracker, lit_ring, theta_group,
brace, brace_label, d_theta,
alt_R_line, theta_mob_opacity_tracker,
)
2018-11-29 17:30:34 -08:00
def enumerate_rings(self):
pass # Skip, for now...
2018-11-29 22:44:08 -08:00
def ask_about_ring_circumference(self):
theta = self.theta_tracker.get_value()
radius = self.sphere_config["radius"]
circle = Circle(
radius=radius * np.sin(theta)
)
circle.shift(radius * np.cos(theta) * OUT)
circle.set_stroke(Color("red"), 5)
to_fade = VGroup(
self.R_label, self.radial_line,
self.brace, self.brace_label
)
self.move_camera(
phi=0 * DEGREES,
theta=-90 * DEGREES,
added_anims=[FadeOut(to_fade)]
)
self.play(ShowCreation(circle))
self.wait()
self.move_camera(
phi=70 * DEGREES,
theta=-70 * DEGREES,
added_anims=[
FadeIn(to_fade),
FadeOut(circle),
]
)
self.wait()
2018-11-29 17:30:34 -08:00
def ask_about_shadow_area(self):
2018-11-29 22:44:08 -08:00
lit_ring = self.lit_ring
lit_ring_copy = lit_ring.copy()
lit_ring_copy.clear_updaters()
all_shadows = self.shadows
all_shadows.set_fill(BLUE_E, 0.5)
for piece in all_shadows.family_members_with_points():
piece.set_stroke(width=0)
radius = self.sphere_config["radius"]
shadow = self.get_shadow(lit_ring)
theta = self.theta_tracker.get_value()
d_theta = self.d_theta
def get_dashed_line(angle):
p0 = np.cos(angle) * OUT + np.sin(angle) * RIGHT
p0 *= radius
p1 = np.array([*p0[:2], 0])
result = DashedLine(p0, p1)
result.set_stroke(WHITE, 1)
result.add_to_back(
result.copy().set_stroke(BLACK, 2)
)
result.set_shade_in_3d(True)
return result
dashed_lines = VGroup(*[
get_dashed_line(t)
for t in [theta, theta + d_theta]
])
self.play(
ReplacementTransform(lit_ring_copy, shadow),
FadeOut(self.R_label),
FadeOut(self.radial_line),
Animation(self.camera.phi_tracker),
*map(ShowCreation, dashed_lines),
run_time=2,
)
self.wait(2)
self.set_variables_as_attrs(
dashed_lines,
lit_shadow=shadow,
)
2018-11-29 17:30:34 -08:00
def ask_about_2_to_1_correspondance(self):
2018-11-29 22:44:08 -08:00
theta_tracker = ValueTracker(0)
get_theta = theta_tracker.get_value
new_lit_ring = always_redraw(
2018-11-29 22:44:08 -08:00
lambda: self.get_ring_from_theta(
self.rings, get_theta()
).copy().set_color(PINK)
)
self.add(new_lit_ring)
for angle in [PI, 0, PI]:
self.play(
theta_tracker.set_value, angle,
Animation(self.camera.phi_tracker),
run_time=3
)
self.remove(new_lit_ring)
self.remove(theta_tracker)
def show_all_shadow_rings(self):
lit_ring_copy = self.lit_ring.copy()
lit_ring_copy.clear_updaters()
self.remove(self.lit_ring)
theta_group_copy = self.theta_group.copy()
theta_group_copy.clear_updaters()
self.remove(self.theta_group, *self.theta_group)
to_fade = VGroup(
theta_group_copy, self.alt_R_line,
self.brace, self.brace_label,
lit_ring_copy, self.lit_shadow,
self.slice_circle,
self.dashed_lines,
)
R_label = self.R_label
radial_line = self.radial_line
R_label.rotate(
-90 * DEGREES,
axis=RIGHT, about_point=radial_line.get_center()
)
shadows = self.shadows
2018-12-26 11:37:07 -08:00
self.set_ring_colors(shadows, [GREY_BROWN, DARK_GREY])
for submob in shadows:
submob.save_state()
shadows.become(self.rings.saved_state[:len(shadows)])
2018-11-29 22:44:08 -08:00
self.play(
FadeOut(to_fade),
2019-02-14 12:06:21 -08:00
LaggedStartMap(FadeIn, shadows),
2018-11-29 22:44:08 -08:00
self.theta_mob_opacity_tracker.set_value, 0,
2018-12-26 11:37:07 -08:00
)
self.play(
2019-02-14 12:06:21 -08:00
LaggedStartMap(Restore, shadows),
2018-11-29 22:44:08 -08:00
ApplyMethod(
self.camera.phi_tracker.set_value, 60 * DEGREES,
),
ApplyMethod(
self.camera.theta_tracker.set_value, -130 * DEGREES,
),
2018-12-26 11:37:07 -08:00
run_time=3
2018-11-29 22:44:08 -08:00
)
self.play(
ShowCreation(radial_line),
FadeIn(R_label),
Animation(self.camera.phi_tracker),
)
self.begin_ambient_camera_rotation()
self.wait()
rings = self.rings
rings_copy = rings.saved_state.copy()
2018-12-26 11:37:07 -08:00
self.set_ring_colors(rings_copy)
2018-11-29 22:44:08 -08:00
self.play(
FadeOut(R_label),
FadeOut(radial_line),
FadeIn(rings_copy)
)
self.remove(rings_copy)
2018-12-26 11:37:07 -08:00
rings.become(rings_copy)
2018-11-29 22:44:08 -08:00
self.add(rings)
2018-11-29 17:30:34 -08:00
def ask_about_global_correspondance(self):
2018-11-29 22:44:08 -08:00
rings = self.rings
2018-12-26 11:37:07 -08:00
self.play(
FadeOut(rings[::2])
)
self.wait(8)
2018-11-29 17:30:34 -08:00
#
def set_ring_colors(self, rings, colors=[BLUE_E, BLUE_D]):
for i, ring in enumerate(rings):
color = colors[i % len(colors)]
2018-12-26 11:37:07 -08:00
ring.set_fill(color, opacity=1)
ring.set_stroke(color, width=0.5, opacity=1)
2018-11-29 22:44:08 -08:00
for piece in ring:
piece.insert_n_curves(4)
2018-11-29 22:44:08 -08:00
piece.on_sphere = True
piece.points = np.array([
*piece.points[3:-1],
*piece.points[:3],
piece.points[3]
])
2018-11-29 17:30:34 -08:00
return rings
def get_shadow(self, mobject):
result = mobject.copy()
result.apply_function(
lambda p: np.array([*p[:2], 0])
)
return result
def get_hemisphere(self, group, vect):
if len(group.submobjects) == 0:
if np.dot(group.get_center(), vect) > 0:
return group
else:
return VMobject()
else:
return VGroup(*[
self.get_hemisphere(submob, vect)
for submob in group
])
def get_northern_hemisphere(self, group):
return self.get_hemisphere(group, OUT)
def get_theta(self, ring):
piece = ring[0]
point = piece.points[3]
return np.arccos(point[2] / get_norm(point))
def get_theta_group(self, theta):
arc = Arc(
start_angle=90 * DEGREES,
angle=-theta,
radius=0.5,
)
arc.rotate(90 * DEGREES, RIGHT, about_point=ORIGIN)
arc.set_stroke(YELLOW, 2)
theta_mob = TexMobject("\\theta")
theta_mob.rotate(90 * DEGREES, RIGHT)
vect = np.cos(theta / 2) * OUT + np.sin(theta / 2) * RIGHT
2018-11-29 22:44:08 -08:00
theta_mob.move_to(
(arc.radius + 0.25) * normalize(vect),
2018-11-29 17:30:34 -08:00
)
theta_mob.set_background_stroke(width=1)
radius = self.sphere_config["radius"]
point = arc.point_from_proportion(1)
radial_line = Line(
ORIGIN, radius * normalize(point)
)
radial_line.set_stroke(WHITE, 2)
return VGroup(arc, radial_line, theta_mob)
def get_ring_from_theta(self, rings, theta):
n_rings = len(rings)
index = min(int((theta / PI) * n_rings), n_rings - 1)
return rings[index]
2018-11-29 22:44:08 -08:00
class SecondProofHigherRes(SecondProof):
CONFIG = {
"sphere_config": {
"resolution": (60, 60),
},
}
2018-12-26 11:37:07 -08:00
class SecondProofHighestRes(SecondProof):
CONFIG = {
"sphere_config": {
"resolution": (120, 120),
},
}
2018-11-29 17:30:34 -08:00
class RangeFrom0To180(Scene):
def construct(self):
angle = Integer(0, unit="^\\circ")
angle.scale(2)
self.add(angle)
self.wait()
self.play(ChangeDecimalToValue(
angle, 180,
run_time=2,
))
self.wait()
2018-12-26 11:37:07 -08:00
class Question1(Scene):
def construct(self):
kwargs = {
"tex_to_color_map": {
"circumference": RED,
}
}
question = TextMobject(
"""
\\small
Question \\#1: What is the circumference of\\\\
one of these rings (in terms of $R$ and $\\theta$)?\\\\
""",
**kwargs
)
prompt = TextMobject(
"""
Multiply this circumference by $R\\,d\\theta$ to \\\\
get an approximation of the ring's area.
""",
**kwargs
)
for words in question, prompt:
words.set_width(FRAME_WIDTH - 1)
self.play(FadeInFromDown(question))
self.wait(2)
for word in question, prompt:
word.circum = word.get_part_by_tex("circumference")
word.remove(word.circum)
self.play(
FadeOutAndShift(question, UP),
FadeInFromDown(prompt),
question.circum.replace, prompt.circum,
run_time=1.5
)
self.wait()
class YouCouldIntegrate(TeacherStudentsScene):
def construct(self):
self.student_says(
"Integrate?",
student_index=2,
bubble_kwargs={"direction": LEFT},
)
self.play(self.teacher.change, "hesitant")
self.wait()
self.teacher_says(
"We'll be a bit \\\\ more Archimedean",
target_mode="speaking"
)
self.change_all_student_modes("confused")
self.wait()
class Question2(Scene):
def construct(self):
question = TextMobject(
"""
Question \\#2: What is the area of the shadow of\\\\
one of these rings? (In terms of $R$, $\\theta$, and $d\\theta$).
""",
tex_to_color_map={
"shadow": YELLOW,
}
)
question.set_width(FRAME_WIDTH - 1)
self.play(FadeInFromDown(question))
self.wait()
class Question3(Scene):
def construct(self):
question = TextMobject("Question \\#3:")
question.to_edge(LEFT)
equation = TextMobject(
"(Shadow area)", "=", "$\\frac{1}{2}$",
"(Area of one of the rings)"
)
equation[0][1:-1].set_color(YELLOW)
equation[3][1:-1].set_color(PINK)
equation.next_to(question, RIGHT)
which_one = TextMobject("Which one?")
# which_one.set_color(YELLOW)
brace = Brace(equation[-1], DOWN, buff=SMALL_BUFF)
which_one.next_to(brace, DOWN, SMALL_BUFF)
self.add(question)
self.play(FadeInFrom(equation))
self.wait()
self.play(
GrowFromCenter(brace),
Write(which_one)
)
self.wait()
class ExtraHint(Scene):
def construct(self):
title = TextMobject("Extra hint")
title.scale(2.5)
title.shift(UP)
equation = TexMobject(
"\\sin(2\\theta) = 2\\sin(\\theta)\\cos(\\theta)"
)
equation.next_to(title, DOWN)
self.add(title, equation)
class Question4(Scene):
def construct(self):
question = TextMobject(
"Question \\#4:",
"Explain how the shadows relate to\\\\"
"every other ring on the sphere.",
tex_to_color_map={
"shadows": YELLOW,
"every other ring": BLUE,
}
)
self.add(question[0])
self.wait()
self.play(FadeInFromDown(question[1:]))
self.wait()
class Question5(Scene):
def construct(self):
question = TextMobject(
"""
Question \\#5: Why does this imply that the \\\\
shadow is $\\frac{1}{4}$ the surface area?
"""
)
self.play(FadeInFromDown(question))
self.wait()
class SpherePatronThanks(Scene):
CONFIG = {
"specific_patrons": [
"1stViewMaths",
"Adrian Robinson",
"Alexis Olson",
"Ali Yahya",
"Andrew Busey",
"Ankalagon",
"Antonio Juarez",
"Art Ianuzzi",
"Arthur Zey",
"Awoo",
"Bernd Sing",
"Boris Veselinovich",
"Brian Staroselsky",
"brian tiger chow",
"Brice Gower",
"Britt Selvitelle",
"Burt Humburg",
"Carla Kirby",
"Charles Southerland",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"Danger Dai",
"Dave B",
"Dave Kester",
"dave nicponski",
"David Clark",
"David Gow",
"Delton Ding",
"Devarsh Desai",
"Devin Scott",
"eaglle",
"Eric Younge",
"Eryq Ouithaqueue",
"Evan Phillips",
"Federico Lebron",
"Florian Chudigiewitsch",
"Giovanni Filippi",
"Graham",
"Hal Hildebrand",
"J",
"Jacob Magnuson",
"Jameel Syed",
"James Hughes",
"Jan Pijpers",
"Jason Hise",
"Jeff Linse",
"Jeff Straathof",
"Jerry Ling",
"John Griffith",
"John Haley",
"John Shaughnessy",
"John V Wertheim",
"Jonathan Eppele",
"Jonathan Wilson",
"Joseph John Cox",
"Joseph Kelly",
"Juan Benet",
"Julian Pulgarin",
"Kai-Siang Ang",
"Kanan Gill",
"Kaustuv DeBiswas",
"L0j1k",
"Linh Tran",
"Luc Ritchie",
"Ludwig Schubert",
"Lukas -krtek.net- Novy",
"Lukas Biewald",
"Magister Mugit",
"Magnus Dahlström",
"Magnus Lysfjord",
"Mark B Bahu",
"Markus Persson",
"Mathew Bramson",
"Mathias Jansson",
"Matt Langford",
"Matt Roveto",
"Matt Russell",
"Matthew Cocke",
"Maurício Collares",
"Mehdi Razavi",
"Michael Faust",
"Michael Hardel",
"MrSneaky",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nathan Jessurun",
"Nero Li",
"Oliver Steele",
"Omar Zrien",
"Peter Ehrnstrom",
"Peter Mcinerney",
"Quantopian",
"Randy C. Will",
"Richard Burgmann",
"Ripta Pasay",
"Rish Kundalia",
"Robert Teed",
"Roobie",
"Roy Larson",
"Ryan Atallah",
"Ryan Williams",
"Scott Walter, Ph.D.",
"Sindre Reino Trosterud",
"soekul",
"Solara570",
"Song Gao",
"Steven Soloway",
"Stevie Metke",
"Ted Suzman",
"Valeriy Skobelev",
"Vassili Philippov",
"Xavier Bernard",
"Yana Chernobilsky",
"Yaw Etse",
"YinYangBalance Asia",
"Zach Cardwell",
],
}
def construct(self):
self.add_title()
self.show_columns()
def add_title(self):
title = TextMobject("Funded by the community, with special thanks to:")
title.set_color(YELLOW)
title.to_edge(UP)
underline = Line(LEFT, RIGHT)
underline.set_width(title.get_width() + MED_SMALL_BUFF)
underline.next_to(title, DOWN, SMALL_BUFF)
title.add(underline)
self.add(title)
self.title = title
def show_columns(self):
random.seed(1)
random.shuffle(self.specific_patrons)
patrons = VGroup(*[
TextMobject(name)
for name in self.specific_patrons
])
columns = VGroup()
column_size = 15
for n in range(0, len(patrons), column_size):
column = patrons[n:n + column_size]
2019-02-04 14:54:25 -08:00
column.arrange(
2018-12-26 11:37:07 -08:00
DOWN,
aligned_edge=LEFT
)
columns.add(column)
columns.set_height(6)
for group in columns[:4], columns[4:]:
for k, column in enumerate(group):
column.move_to(
6.5 * LEFT + 3.75 * k * RIGHT + 2.5 * UP,
UL
)
self.add(columns[:4])
self.wait()
for k in range(4):
self.play(
FadeOut(columns[k]),
FadeIn(columns[k + 4]),
)
self.wait()
class EndScreen(PatreonEndScreen):
CONFIG = {
"thanks_words": "",
}
class ForThoseStillAround(Scene):
def construct(self):
words = TextMobject("Still here?")
words.scale(1.5)
url = TextMobject("3blue1brown.com/store")
# url.scale(1.5)
url.to_edge(UP, buff=MED_SMALL_BUFF)
self.play(Write(words))
self.wait()
self.play(ReplacementTransform(words, url))
self.wait()
class PatronWords(Scene):
def construct(self):
words = TextMobject("\\$2+ Patrons get \\\\ 50\\% off")
words.to_corner(UL)
words.set_color(RED)
self.add(words)
class PlushMe(TeacherStudentsScene):
def construct(self):
self.student_says("Plushie me?")
self.change_student_modes("happy", None, "happy")
self.play(self.teacher.change, "confused")
self.wait()
self.teacher_says("...why?", target_mode="maybe")
self.wait(2)
class Thumbnail(SpecialThreeDScene):
CONFIG = {
"camera_config": {
"light_source_start_point": [-10, 5, 7],
}
}
def construct(self):
radius = 1.75
sphere = self.get_sphere(radius=radius)
sphere.rotate(70 * DEGREES, LEFT)
sphere.set_fill(BLUE_E)
sphere.set_stroke(WHITE, 0.5)
circles = VGroup(*[
Circle(radius=radius)
for x in range(4)
])
circles.set_stroke(WHITE, 2)
circles.set_fill(BLUE_E, 1)
circles[0].set_fill(GREY_BROWN)
2019-02-04 14:54:25 -08:00
circles.arrange_in_grid()
2018-12-26 11:37:07 -08:00
for circle in circles:
formula = TexMobject("\\pi", "R", "^2")
formula.set_color_by_tex("R", YELLOW)
formula.scale(2)
formula.move_to(circle)
circle.add(formula)
equals = TexMobject("=")
equals.scale(3)
group = VGroup(sphere, equals, circles)
2019-02-04 14:54:25 -08:00
group.arrange(RIGHT, buff=MED_SMALL_BUFF)
2018-12-26 11:37:07 -08:00
equals.shift(3 * SMALL_BUFF * RIGHT)
why = TextMobject("Why?!")
why.set_color(YELLOW)
why.scale(2.5)
why.next_to(sphere, UP)
sa_formula = TexMobject("4\\pi", "R", "^2")
sa_formula.set_color_by_tex("R", YELLOW)
sa_formula.scale(2)
sa_formula.next_to(sphere, DOWN)
self.camera.distance_tracker.set_value(100)
self.add(sphere, equals, circles, why, sa_formula)