mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
1236 lines
36 KiB
Python
1236 lines
36 KiB
Python
from manimlib.imports import *
|
|
from old_projects.lost_lecture import GeometryProofLand
|
|
from old_projects.quaternions import SpecialThreeDScene
|
|
from old_projects.uncertainty import Flash
|
|
|
|
|
|
class Introduction(TeacherStudentsScene):
|
|
CONFIG = {
|
|
"random_seed": 2,
|
|
}
|
|
|
|
def construct(self):
|
|
self.play(
|
|
Animation(VectorizedPoint(self.hold_up_spot)),
|
|
self.teacher.change, "raise_right_hand",
|
|
)
|
|
self.change_student_modes(
|
|
"angry", "sassy", "pleading"
|
|
)
|
|
self.wait()
|
|
|
|
movements = []
|
|
for student in self.students:
|
|
student.center_tracker = VectorizedPoint()
|
|
student.center_tracker.move_to(student)
|
|
student.center_tracker.save_state()
|
|
student.add_updater(
|
|
lambda m: m.move_to(m.center_tracker)
|
|
)
|
|
always_shift(
|
|
student.center_tracker,
|
|
direction=DOWN + 3 * LEFT,
|
|
rate=1.5 * random.random()
|
|
)
|
|
movements.append(student.center_tracker)
|
|
self.add(*movements)
|
|
self.change_student_modes(
|
|
"pondering", "sad", "concerned_musician",
|
|
look_at_arg=10 * LEFT + 2 * DOWN
|
|
)
|
|
self.teacher_says(
|
|
"Wait, wait, wait!",
|
|
target_mode="surprised"
|
|
)
|
|
self.remove(*movements)
|
|
self.play(
|
|
self.get_student_changes(*3 * ["hesitant"]),
|
|
*[
|
|
Restore(student.center_tracker)
|
|
for student in self.students
|
|
]
|
|
)
|
|
|
|
|
|
class StudentsWatching(TeacherStudentsScene):
|
|
def construct(self):
|
|
self.play(
|
|
self.teacher.change, "raise_right_hand",
|
|
self.get_student_changes(
|
|
*3 * ["thinking"],
|
|
look_at_arg=self.screen
|
|
),
|
|
VFadeIn(self.pi_creatures, run_time=2)
|
|
)
|
|
self.wait(5)
|
|
|
|
|
|
class UnexpectedConnection(Scene):
|
|
def construct(self):
|
|
primes = TexMobject(
|
|
"2,", "3,", "5,", "7,", "11,", "13,", "17,", "\\dots"
|
|
)
|
|
primes.move_to(2.5 * UP)
|
|
|
|
circle = Circle(
|
|
color=YELLOW,
|
|
stroke_width=1,
|
|
radius=1.5,
|
|
)
|
|
circle.shift(1.5 * DOWN)
|
|
center = circle.get_center()
|
|
center_dot = Dot(center)
|
|
radius = Line(center, circle.get_right())
|
|
radius.set_stroke(WHITE, 3)
|
|
|
|
arrow = DoubleArrow(primes, circle)
|
|
arrow.tip[1].shift(SMALL_BUFF * UP)
|
|
arrow.save_state()
|
|
arrow.rotate(90 * DEGREES)
|
|
arrow.scale(1.5)
|
|
arrow.fade(1)
|
|
|
|
formula = TexMobject(
|
|
"\\frac{\\pi^2}{6} = \\prod_{p \\text{ prime}}"
|
|
"\\frac{1}{1 - p^{-2}}"
|
|
)
|
|
formula.next_to(arrow.get_center(), RIGHT)
|
|
|
|
def get_arc():
|
|
angle = radius.get_angle()
|
|
return Arc(
|
|
start_angle=0,
|
|
angle=angle,
|
|
radius=circle.radius,
|
|
stroke_color=YELLOW,
|
|
stroke_width=5
|
|
).shift(center)
|
|
|
|
arc = always_redraw(get_arc)
|
|
|
|
decimal = DecimalNumber(0)
|
|
decimal.add_updater(
|
|
lambda d: d.move_to(interpolate(
|
|
radius.get_start(),
|
|
radius.get_end(),
|
|
1.5,
|
|
)),
|
|
)
|
|
decimal.add_updater(
|
|
lambda d: d.set_value(radius.get_angle())
|
|
)
|
|
pi = TexMobject("\\pi")
|
|
pi.scale(2)
|
|
pi.next_to(circle, LEFT)
|
|
|
|
self.add(circle, radius, center_dot, decimal, arc)
|
|
self.play(
|
|
Rotate(radius, PI - 1e-7, about_point=center),
|
|
LaggedStartMap(FadeInFromDown, primes),
|
|
run_time=4
|
|
)
|
|
self.remove(decimal)
|
|
self.add(pi)
|
|
self.wait()
|
|
self.play(
|
|
Restore(arrow),
|
|
FadeInFrom(formula, LEFT)
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class MapOfVideo(MovingCameraScene):
|
|
def construct(self):
|
|
images = Group(
|
|
ImageMobject("NecklaceThumbnail"),
|
|
ImageMobject("BorsukUlamThumbnail"),
|
|
ImageMobject("TopologyProofThumbnail"),
|
|
ImageMobject("ContinuousNecklaceThumbnail"),
|
|
ImageMobject("NecklaceSphereAssociationThumbnail")
|
|
)
|
|
for image in images:
|
|
rect = SurroundingRectangle(image, buff=0)
|
|
rect.set_stroke(WHITE, 3)
|
|
image.add(rect)
|
|
|
|
image_line = Group(*images[:2], *images[3:])
|
|
image_line.arrange(RIGHT, buff=LARGE_BUFF)
|
|
images[2].next_to(image_line, DOWN, buff=1.5)
|
|
images.set_width(FRAME_WIDTH - 1)
|
|
images.to_edge(UP, buff=LARGE_BUFF)
|
|
|
|
arrows = VGroup(
|
|
Arrow(images[0], images[1], buff=SMALL_BUFF),
|
|
Arrow(
|
|
images[1].get_corner(DR) + 0.5 * LEFT,
|
|
images[2].get_top() + 0.5 * LEFT,
|
|
),
|
|
Arrow(
|
|
images[2].get_top() + 0.5 * RIGHT,
|
|
images[3].get_corner(DL) + 0.5 * RIGHT,
|
|
),
|
|
Arrow(images[3], images[4], buff=SMALL_BUFF),
|
|
)
|
|
|
|
self.play(LaggedStartMap(FadeInFromDown, images, run_time=4))
|
|
self.play(LaggedStartMap(GrowArrow, arrows))
|
|
self.wait()
|
|
group = Group(images, arrows)
|
|
for image in images:
|
|
group.save_state()
|
|
group.generate_target()
|
|
group.target.shift(-image.get_center())
|
|
group.target.scale(
|
|
FRAME_WIDTH / image.get_width(),
|
|
about_point=ORIGIN,
|
|
)
|
|
|
|
self.play(MoveToTarget(group, run_time=3))
|
|
self.wait()
|
|
self.play(Restore(group, run_time=3))
|
|
|
|
def get_curved_arrow(self, *points):
|
|
line = VMobject()
|
|
line.set_points(points)
|
|
tip = Arrow(points[-2], points[-1], buff=SMALL_BUFF).tip
|
|
line.pointwise_become_partial(line, 0, 0.9)
|
|
line.add(tip)
|
|
return line
|
|
|
|
|
|
class MathIsDeep(PiCreatureScene):
|
|
def construct(self):
|
|
words = TextMobject(
|
|
"Math", "is", "deep"
|
|
)
|
|
words.scale(2)
|
|
words.to_edge(UP)
|
|
math = words[0].copy()
|
|
math[1].remove(math[1][1])
|
|
math.set_fill(opacity=0)
|
|
math.set_stroke(width=0, background=True)
|
|
numbers = [13, 1, 20, 8]
|
|
num_mobs = VGroup(*[Integer(d) for d in numbers])
|
|
num_mobs.arrange(RIGHT, buff=MED_LARGE_BUFF)
|
|
num_mobs.next_to(math, DOWN, buff=1.5)
|
|
num_mobs.set_color(YELLOW)
|
|
top_arrows = VGroup(*[
|
|
Arrow(c.get_bottom(), n.get_top())
|
|
for c, n in zip(math, num_mobs)
|
|
])
|
|
n_sum = Integer(sum(numbers))
|
|
n_sum.scale(1.5)
|
|
n_sum.next_to(num_mobs, DOWN, buff=1.5)
|
|
low_arrows = VGroup(*[
|
|
Arrow(n.get_bottom(), n_sum.get_top())
|
|
for n in num_mobs
|
|
])
|
|
VGroup(top_arrows, low_arrows).set_color(WHITE)
|
|
|
|
n_sum_border = n_sum.deepcopy()
|
|
n_sum_border.set_fill(opacity=0)
|
|
n_sum_border.set_stroke(YELLOW, width=1)
|
|
n_sum_border.set_stroke(width=0, background=True)
|
|
|
|
# pre_num_mobs = num_mobs.copy()
|
|
# for pn, letter in zip(pre_num_mobs, math):
|
|
# pn.fade(1)
|
|
# pn.set_color(RED)
|
|
# pn.move_to(letter)
|
|
# num_mobs[1].add_subpath(num_mobs[1].points)
|
|
|
|
self.play(
|
|
LaggedStartMap(
|
|
FadeInFromLarge, words,
|
|
scale_factor=1.5,
|
|
run_time=0.6,
|
|
lag_ratio=0.6,
|
|
),
|
|
self.pi_creature.change, "pondering"
|
|
)
|
|
self.play(
|
|
TransformFromCopy(math, num_mobs),
|
|
*map(GrowArrow, top_arrows),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
TransformFromCopy(num_mobs, VGroup(n_sum)),
|
|
self.pi_creature.change, "thinking",
|
|
*map(GrowArrow, low_arrows),
|
|
)
|
|
self.play(LaggedStartMap(ShowCreationThenDestruction, n_sum_border))
|
|
self.play(Blink(self.pi_creature))
|
|
self.wait()
|
|
|
|
|
|
class MinimizeSharding(Scene):
|
|
def construct(self):
|
|
piece_groups = VGroup(*[
|
|
VGroup(*[
|
|
self.get_piece()
|
|
for x in range(3)
|
|
]).arrange(RIGHT, buff=SMALL_BUFF)
|
|
for y in range(4)
|
|
]).arrange(RIGHT, buff=SMALL_BUFF)
|
|
|
|
self.add(piece_groups)
|
|
self.play(*[
|
|
ApplyMethod(mob.space_out_submobjects, 0.7)
|
|
for mob in piece_groups
|
|
])
|
|
self.wait()
|
|
group1 = piece_groups[:2]
|
|
group2 = piece_groups[2:]
|
|
self.play(
|
|
group1.arrange, DOWN,
|
|
group1.next_to, ORIGIN, LEFT, LARGE_BUFF,
|
|
group2.arrange, DOWN,
|
|
group2.next_to, ORIGIN, RIGHT, LARGE_BUFF,
|
|
)
|
|
self.wait()
|
|
|
|
def get_piece(self):
|
|
jagged_spots = [
|
|
ORIGIN, 2 * UP + RIGHT, 4 * UP + LEFT, 6 * UP,
|
|
]
|
|
corners = list(it.chain(
|
|
jagged_spots,
|
|
[6 * UP + 10 * RIGHT],
|
|
[
|
|
p + 10 * RIGHT
|
|
for p in reversed(jagged_spots)
|
|
],
|
|
[ORIGIN]
|
|
))
|
|
piece = VMobject().set_points_as_corners(corners)
|
|
piece.set_width(1)
|
|
piece.center()
|
|
piece.set_stroke(WHITE, width=0.5)
|
|
piece.set_fill(BLUE, opacity=1)
|
|
return piece
|
|
|
|
|
|
class Antipodes(Scene):
|
|
def construct(self):
|
|
word = TextMobject("``Antipodes''")
|
|
word.set_width(FRAME_WIDTH - 1)
|
|
word.set_color(MAROON_B)
|
|
self.play(Write(word))
|
|
self.wait()
|
|
|
|
|
|
class TopologyWordBreak(Scene):
|
|
def construct(self):
|
|
word = TextMobject("Topology")
|
|
word.scale(2)
|
|
colors = [BLUE, YELLOW, RED]
|
|
classes = VGroup(*[VGroup() for x in range(3)])
|
|
for letter in word:
|
|
genus = len(letter.submobjects)
|
|
letter.target_color = colors[genus]
|
|
letter.generate_target()
|
|
letter.target.set_color(colors[genus])
|
|
classes[genus].add(letter.target)
|
|
signs = VGroup()
|
|
for group in classes:
|
|
new_group = VGroup()
|
|
for elem in group[:-1]:
|
|
new_group.add(elem)
|
|
sign = TexMobject("\\simeq")
|
|
new_group.add(sign)
|
|
signs.add(sign)
|
|
new_group.add(group[-1])
|
|
group.submobjects = list(new_group.submobjects)
|
|
group.arrange(RIGHT)
|
|
|
|
word[2].target.shift(0.1 * DOWN)
|
|
word[7].target.shift(0.1 * DOWN)
|
|
|
|
classes.arrange(DOWN, buff=LARGE_BUFF, aligned_edge=LEFT)
|
|
classes.shift(2 * RIGHT)
|
|
|
|
genus_labels = VGroup(*[
|
|
TextMobject("Genus %d:" % d).scale(1.5).next_to(
|
|
classes[d], LEFT, MED_LARGE_BUFF
|
|
)
|
|
for d in range(3)
|
|
])
|
|
genus_labels.shift(SMALL_BUFF * UP)
|
|
|
|
self.play(Write(word))
|
|
self.play(LaggedStartMap(
|
|
ApplyMethod, word,
|
|
lambda m: (m.set_color, m.target_color),
|
|
run_time=1
|
|
))
|
|
self.play(
|
|
LaggedStartMap(MoveToTarget, word),
|
|
LaggedStartMap(FadeIn, signs),
|
|
LaggedStartMap(FadeInFromDown, genus_labels),
|
|
)
|
|
self.wait(3)
|
|
|
|
|
|
class TopologyProofLand(GeometryProofLand):
|
|
CONFIG = {
|
|
"text": "Topology proof land"
|
|
}
|
|
|
|
|
|
class GreenLine(Scene):
|
|
def construct(self):
|
|
self.add(Line(LEFT, RIGHT, color=GREEN))
|
|
|
|
|
|
class Thief(Scene):
|
|
def construct(self):
|
|
self.play(Write(TextMobject("Thief")))
|
|
self.wait()
|
|
|
|
|
|
class FunctionGInSymbols(Scene):
|
|
def construct(self):
|
|
p_tex = "\\vec{\\textbf{p}}"
|
|
neg_p_tex = "-\\vec{\\textbf{p}}"
|
|
|
|
def color_tex(tex_mob):
|
|
pairs = [
|
|
(p_tex, YELLOW),
|
|
(neg_p_tex, RED),
|
|
("{g}", GREEN),
|
|
]
|
|
for tex, color in pairs:
|
|
tex_mob.set_color_by_tex(
|
|
tex, color, substring=False
|
|
)
|
|
|
|
f_of_p = TexMobject("f", "(", p_tex, ")")
|
|
f_of_p.shift(2.5 * LEFT + 2.5 * UP)
|
|
f_of_neg_p = TexMobject("f", "(", neg_p_tex, ")")
|
|
g_of_p = TexMobject("g", "(", p_tex, ")")
|
|
g_of_p[0].set_color(YELLOW)
|
|
for mob in f_of_p, f_of_neg_p, g_of_p:
|
|
color_tex(mob)
|
|
dec_rhs = DecimalMatrix([[-0.9], [0.5]])
|
|
dec_rhs.next_to(f_of_p, RIGHT)
|
|
minus = TexMobject("-")
|
|
equals = TexMobject("=")
|
|
equals.next_to(f_of_p, RIGHT)
|
|
zero_zero = IntegerMatrix([[0], [0]])
|
|
|
|
for matrix in dec_rhs, zero_zero:
|
|
matrix.space_out_submobjects(0.8)
|
|
matrix.brackets.scale(0.9)
|
|
matrix.next_to(equals, RIGHT)
|
|
|
|
f_of_neg_p.next_to(equals, RIGHT)
|
|
|
|
f = f_of_p.get_part_by_tex("f")
|
|
p = f_of_p.get_part_by_tex(p_tex)
|
|
f_brace = Brace(f, UP, buff=SMALL_BUFF)
|
|
f_brace.add(f_brace.get_text("Continuous function"))
|
|
p_brace = Brace(p, DOWN, buff=SMALL_BUFF)
|
|
p_brace.add(p_brace.get_text("Sphere point").match_color(p))
|
|
|
|
f_of_p.save_state()
|
|
f_of_p.space_out_submobjects(2)
|
|
f_of_p.scale(2)
|
|
f_of_p.fade(1)
|
|
|
|
self.play(f_of_p.restore)
|
|
self.play(GrowFromCenter(f_brace))
|
|
self.wait()
|
|
self.play(GrowFromCenter(p_brace))
|
|
self.wait()
|
|
self.play(
|
|
FadeInFromDown(equals),
|
|
Write(dec_rhs),
|
|
FadeOut(f_brace),
|
|
FadeOut(p_brace),
|
|
)
|
|
self.wait(2)
|
|
self.play(WiggleOutThenIn(f))
|
|
self.wait()
|
|
self.play(
|
|
FadeOutAndShift(dec_rhs, DOWN),
|
|
FadeInFromDown(f_of_neg_p)
|
|
)
|
|
self.wait()
|
|
|
|
# Rearrange
|
|
f_of_neg_p.generate_target()
|
|
f_of_p.generate_target()
|
|
group = VGroup(f_of_p.target, minus, f_of_neg_p.target)
|
|
group.arrange(RIGHT, buff=SMALL_BUFF)
|
|
group.next_to(equals, LEFT)
|
|
|
|
self.play(
|
|
MoveToTarget(f_of_p, path_arc=PI),
|
|
MoveToTarget(f_of_neg_p, path_arc=-PI),
|
|
FadeInFromLarge(minus),
|
|
FadeInFrom(zero_zero, LEFT)
|
|
)
|
|
self.wait()
|
|
|
|
# Define g
|
|
def_eq = TexMobject(":=")
|
|
def_eq.next_to(f_of_p, LEFT)
|
|
g_of_p.next_to(def_eq, LEFT)
|
|
rect = SurroundingRectangle(VGroup(g_of_p, f_of_neg_p))
|
|
rect.set_stroke(width=1)
|
|
seeking_text = TexMobject(
|
|
"\\text{Looking for }", p_tex, "\\text{ where}"
|
|
)
|
|
color_tex(seeking_text)
|
|
seeking_text.next_to(zero_zero, DOWN, MED_LARGE_BUFF)
|
|
seeking_text.to_edge(LEFT)
|
|
g_equals_zero = VGroup(
|
|
g_of_p.copy(), equals, zero_zero
|
|
)
|
|
g_equals_zero.generate_target()
|
|
g_equals_zero.target.arrange(RIGHT, SMALL_BUFF)
|
|
g_equals_zero.target.next_to(seeking_text, DOWN)
|
|
|
|
self.play(
|
|
FadeInFromLarge(g_of_p),
|
|
FadeInFrom(def_eq, LEFT)
|
|
)
|
|
self.play(
|
|
FadeInFromDown(seeking_text),
|
|
MoveToTarget(g_equals_zero)
|
|
)
|
|
self.play(ShowCreation(rect))
|
|
self.wait()
|
|
self.play(FadeOut(rect))
|
|
|
|
# Show g is odd
|
|
g_of_neg_p = TexMobject("{g}", "(", neg_p_tex, ")")
|
|
eq2 = TexMobject("=")
|
|
rhs = TexMobject(
|
|
"f", "(", neg_p_tex, ")", "-",
|
|
"f", "(", p_tex, ")", "=",
|
|
"-", "{g}", "(", p_tex, ")",
|
|
)
|
|
for mob in g_of_neg_p, rhs:
|
|
color_tex(mob)
|
|
g_of_neg_p.next_to(g_of_p, DOWN, aligned_edge=LEFT, buff=LARGE_BUFF)
|
|
eq2.next_to(g_of_neg_p, RIGHT, SMALL_BUFF)
|
|
rhs.next_to(eq2, RIGHT, SMALL_BUFF)
|
|
neg_g_of_p = rhs[-5:]
|
|
neg_g_of_p.save_state()
|
|
neg_g_of_p.next_to(eq2, RIGHT, SMALL_BUFF)
|
|
|
|
self.play(
|
|
FadeIn(g_of_neg_p),
|
|
FadeIn(eq2),
|
|
FadeIn(neg_g_of_p),
|
|
VGroup(seeking_text, g_equals_zero).shift, 1.5 * DOWN
|
|
)
|
|
self.wait()
|
|
self.play(ShowCreationThenFadeAround(g_of_neg_p[2]))
|
|
self.wait()
|
|
self.play(ShowCreationThenFadeAround(neg_g_of_p))
|
|
self.wait()
|
|
self.play(neg_g_of_p.restore)
|
|
rects = VGroup(*map(SurroundingRectangle, [f_of_p, f_of_neg_p]))
|
|
self.play(LaggedStartMap(
|
|
ShowCreationThenDestruction, rects,
|
|
lag_ratio=0.8
|
|
))
|
|
self.play(
|
|
TransformFromCopy(f_of_p, rhs[5:9]),
|
|
TransformFromCopy(f_of_neg_p, rhs[:4]),
|
|
FadeIn(rhs[4]),
|
|
FadeIn(rhs[-6]),
|
|
)
|
|
self.wait()
|
|
|
|
|
|
class FunctionGInputSpace(SpecialThreeDScene):
|
|
def setup(self):
|
|
self.init_tracked_point()
|
|
|
|
sphere = self.get_sphere()
|
|
sphere.set_fill(BLUE_E, opacity=0.5)
|
|
self.sphere = sphere
|
|
|
|
self.set_camera_orientation(
|
|
phi=70 * DEGREES,
|
|
theta=-120 * DEGREES,
|
|
)
|
|
self.begin_ambient_camera_rotation(rate=0.02)
|
|
|
|
self.init_dot()
|
|
|
|
self.add(ThreeDAxes())
|
|
|
|
def construct(self):
|
|
self.show_input_dot()
|
|
self.show_start_path()
|
|
self.show_antipodal_point()
|
|
self.show_equator()
|
|
self.deform_towards_north_pole()
|
|
|
|
def show_input_dot(self):
|
|
sphere = self.sphere
|
|
dot = self.dot
|
|
point_mob = self.tracked_point
|
|
start_point = self.get_start_point()
|
|
|
|
arrow = Arrow(
|
|
start_point + (LEFT + OUT + UP), start_point,
|
|
color=BLUE,
|
|
buff=MED_LARGE_BUFF,
|
|
)
|
|
arrow.rotate(90 * DEGREES, axis=arrow.get_vector())
|
|
arrow.add_to_back(arrow.copy().set_stroke(BLACK, 5))
|
|
|
|
p_label = self.p_label = TexMobject("\\vec{\\textbf{p}}")
|
|
p_label.set_color(YELLOW)
|
|
p_label.next_to(arrow.get_start(), OUT, buff=0.3)
|
|
p_label.set_shade_in_3d(True)
|
|
|
|
self.play(Write(sphere, run_time=3))
|
|
self.add(dot)
|
|
self.add_fixed_orientation_mobjects(p_label)
|
|
self.play(
|
|
point_mob.move_to, start_point,
|
|
GrowArrow(arrow),
|
|
FadeInFrom(p_label, IN)
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
arrow.scale, 0, {"about_point": arrow.get_end()},
|
|
p_label.next_to, dot, OUT + LEFT, SMALL_BUFF
|
|
)
|
|
p_label.add_updater(lambda p: p.next_to(dot, OUT + LEFT, SMALL_BUFF))
|
|
self.wait(4)
|
|
|
|
def show_start_path(self):
|
|
path = self.get_start_path()
|
|
self.draw_path(path, uncreate=True)
|
|
self.wait()
|
|
|
|
def show_antipodal_point(self):
|
|
path = self.get_antipodal_path()
|
|
end_dot = always_redraw(
|
|
lambda: self.get_dot(
|
|
path[-1].point_from_proportion(1)
|
|
).set_color(RED)
|
|
)
|
|
|
|
neg_p = TexMobject("-\\vec{\\textbf{p}}")
|
|
neg_p.add_updater(
|
|
lambda p: p.next_to(end_dot, UP + RIGHT + IN)
|
|
)
|
|
neg_p.set_color(RED)
|
|
neg_p.set_shade_in_3d(True)
|
|
|
|
self.move_camera(
|
|
phi=100 * DEGREES,
|
|
theta=30 * DEGREES,
|
|
added_anims=[ShowCreation(path)],
|
|
run_time=4,
|
|
)
|
|
self.wait()
|
|
self.add_fixed_orientation_mobjects(neg_p)
|
|
self.play(
|
|
FadeInFromLarge(end_dot),
|
|
Write(neg_p)
|
|
)
|
|
self.wait(4)
|
|
self.move_camera(
|
|
phi=70 * DEGREES,
|
|
theta=-120 * DEGREES,
|
|
run_time=2
|
|
)
|
|
self.wait(7)
|
|
# Flip
|
|
self.move_camera(
|
|
phi=100 * DEGREES,
|
|
theta=30 * DEGREES,
|
|
run_time=2,
|
|
)
|
|
self.wait(7)
|
|
self.move_camera(
|
|
phi=70 * DEGREES,
|
|
theta=-120 * DEGREES,
|
|
added_anims=[
|
|
FadeOut(end_dot),
|
|
FadeOut(neg_p),
|
|
FadeOut(path),
|
|
],
|
|
run_time=2,
|
|
)
|
|
|
|
def show_equator(self):
|
|
point_mob = self.tracked_point
|
|
equator = self.get_lat_line()
|
|
|
|
self.play(point_mob.move_to, equator[0].point_from_proportion(0))
|
|
self.play(ShowCreation(equator, run_time=4))
|
|
for x in range(2):
|
|
self.play(
|
|
Rotate(point_mob, PI, about_point=ORIGIN, axis=OUT),
|
|
run_time=4
|
|
)
|
|
self.wait(3)
|
|
self.play(
|
|
FadeOut(self.dot),
|
|
FadeOut(self.p_label),
|
|
)
|
|
|
|
self.equator = equator
|
|
|
|
def deform_towards_north_pole(self):
|
|
equator = self.equator
|
|
|
|
self.play(UpdateFromAlphaFunc(
|
|
equator,
|
|
lambda m, a: m.become(self.get_lat_line(a * PI / 2)),
|
|
run_time=16
|
|
))
|
|
self.wait()
|
|
|
|
#
|
|
def init_tracked_point(self):
|
|
self.tracked_point = VectorizedPoint([0, 0, 2])
|
|
self.tracked_point.add_updater(
|
|
lambda p: p.move_to(2 * normalize(p.get_center()))
|
|
)
|
|
self.add(self.tracked_point)
|
|
|
|
def init_dot(self):
|
|
self.dot = always_redraw(
|
|
lambda: self.get_dot(self.tracked_point.get_center())
|
|
)
|
|
|
|
def get_start_path(self):
|
|
path = ParametricFunction(
|
|
lambda t: np.array([
|
|
-np.sin(TAU * t + TAU / 4),
|
|
np.cos(2 * TAU * t + TAU / 4),
|
|
0
|
|
]),
|
|
color=RED
|
|
)
|
|
path.scale(0.5)
|
|
path.shift(0.5 * OUT)
|
|
path.rotate(60 * DEGREES, RIGHT, about_point=ORIGIN)
|
|
path.shift(
|
|
self.get_start_point() - path.point_from_proportion(0)
|
|
)
|
|
path.apply_function(lambda p: 2 * normalize(p))
|
|
return path
|
|
|
|
def get_antipodal_path(self):
|
|
start = self.get_start_point()
|
|
path = ParametricFunction(
|
|
lambda t: 2.03 * np.array([
|
|
0,
|
|
np.sin(PI * t),
|
|
np.cos(PI * t),
|
|
]),
|
|
color=YELLOW
|
|
)
|
|
path.apply_matrix(z_to_vector(start))
|
|
|
|
dashed_path = DashedVMobject(path)
|
|
dashed_path.set_shade_in_3d(True)
|
|
|
|
return dashed_path
|
|
|
|
def get_lat_line(self, lat=0):
|
|
equator = ParametricFunction(lambda t: 2.03 * np.array([
|
|
np.cos(lat) * np.sin(TAU * t),
|
|
np.cos(lat) * (-np.cos(TAU * t)),
|
|
np.sin(lat)
|
|
]))
|
|
equator.rotate(-90 * DEGREES)
|
|
dashed_equator = DashedVMobject(
|
|
equator,
|
|
num_dashes=40,
|
|
color=RED,
|
|
)
|
|
dashed_equator.set_shade_in_3d(True)
|
|
return dashed_equator
|
|
|
|
def draw_path(self, path,
|
|
run_time=4,
|
|
dot_follow=True,
|
|
uncreate=False,
|
|
added_anims=None
|
|
):
|
|
added_anims = added_anims or []
|
|
point_mob = self.tracked_point
|
|
anims = [ShowCreation(path)]
|
|
if dot_follow:
|
|
anims.append(UpdateFromFunc(
|
|
point_mob,
|
|
lambda p: p.move_to(path.point_from_proportion(1))
|
|
))
|
|
self.add(path, self.dot)
|
|
self.play(*anims, run_time=run_time)
|
|
|
|
if uncreate:
|
|
self.wait()
|
|
self.play(
|
|
Uncreate(path),
|
|
run_time=run_time
|
|
)
|
|
|
|
def modify_path(self, path):
|
|
return path
|
|
|
|
def get_start_point(self):
|
|
return 2 * normalize([-1, -1, 1])
|
|
|
|
def get_dot(self, point):
|
|
dot = Dot(color=WHITE)
|
|
dot.shift(2.05 * OUT)
|
|
dot.apply_matrix(z_to_vector(normalize(point)))
|
|
dot.set_shade_in_3d(True)
|
|
return dot
|
|
|
|
|
|
class FunctionGOutputSpace(FunctionGInputSpace):
|
|
def construct(self):
|
|
self.show_input_dot()
|
|
self.show_start_path()
|
|
self.show_antipodal_point()
|
|
self.show_equator()
|
|
self.deform_towards_north_pole()
|
|
|
|
def setup(self):
|
|
axes = self.axes = Axes(
|
|
x_min=-2.5,
|
|
x_max=2.5,
|
|
y_min=-2.5,
|
|
y_max=2.5,
|
|
number_line_config={'unit_size': 1.5}
|
|
)
|
|
for axis in axes:
|
|
numbers = list(range(-2, 3))
|
|
numbers.remove(0)
|
|
axis.add_numbers(*numbers)
|
|
|
|
self.init_tracked_point()
|
|
self.init_dot()
|
|
|
|
def show_input_dot(self):
|
|
axes = self.axes
|
|
dot = self.dot
|
|
point_mob = self.tracked_point
|
|
|
|
point_mob.move_to(self.get_start_point())
|
|
self.add(dot)
|
|
self.update_mobjects(0)
|
|
self.remove(dot)
|
|
|
|
p_tex = "\\vec{\\textbf{p}}"
|
|
fp_label = self.fp_label = TexMobject("f(", p_tex, ")")
|
|
fp_label.set_color_by_tex(p_tex, YELLOW)
|
|
|
|
self.play(Write(axes, run_time=3))
|
|
self.wait(3)
|
|
dc = dot.copy()
|
|
self.play(
|
|
FadeInFrom(dc, 2 * UP, remover=True),
|
|
UpdateFromFunc(fp_label, lambda fp: fp.next_to(dc, UL, SMALL_BUFF))
|
|
)
|
|
self.add(dot)
|
|
fp_label.add_updater(
|
|
lambda fp: fp.next_to(dot, UL, SMALL_BUFF)
|
|
)
|
|
self.wait(2)
|
|
|
|
def draw_path(self, path,
|
|
run_time=4,
|
|
dot_follow=True,
|
|
uncreate=False,
|
|
added_anims=None
|
|
):
|
|
added_anims = added_anims or []
|
|
point_mob = self.tracked_point
|
|
shadow_path = path.deepcopy().fade(1)
|
|
flat_path = self.modify_path(path)
|
|
anims = [
|
|
ShowCreation(flat_path),
|
|
ShowCreation(shadow_path),
|
|
]
|
|
if dot_follow:
|
|
anims.append(UpdateFromFunc(
|
|
point_mob,
|
|
lambda p: p.move_to(shadow_path.point_from_proportion(1))
|
|
))
|
|
self.add(flat_path, self.dot)
|
|
self.play(*anims, run_time=run_time)
|
|
|
|
if uncreate:
|
|
self.wait()
|
|
self.remove(shadow_path)
|
|
self.play(
|
|
Uncreate(flat_path),
|
|
run_time=run_time
|
|
)
|
|
|
|
def show_antipodal_point(self):
|
|
dot = self.dot
|
|
pre_path = VMobject().set_points_smoothly([
|
|
ORIGIN, DOWN, DOWN + 2 * RIGHT,
|
|
3 * RIGHT + 0.5 * UP, 0.5 * RIGHT, ORIGIN
|
|
])
|
|
pre_path.rotate(-45 * DEGREES, about_point=ORIGIN)
|
|
pre_path.shift(dot.get_center())
|
|
path = DashedVMobject(pre_path)
|
|
|
|
fp_label = self.fp_label
|
|
equals = TexMobject("=")
|
|
equals.next_to(fp_label, RIGHT, SMALL_BUFF)
|
|
f_neg_p = TexMobject("f(", "-\\vec{\\textbf{p}}", ")")
|
|
f_neg_p[1].set_color(RED)
|
|
f_neg_p.next_to(equals, RIGHT)
|
|
|
|
gp_label = TexMobject("g", "(", "\\vec{\\textbf{p}}", ")")
|
|
gp_label[0].set_color(GREEN)
|
|
gp_label[2].set_color(YELLOW)
|
|
gp_label.add_updater(lambda m: m.next_to(dot, UL, SMALL_BUFF))
|
|
self.gp_label = gp_label
|
|
# gp_label.next_to(Dot(ORIGIN), UL, SMALL_BUFF)
|
|
|
|
self.play(ShowCreation(path, run_time=4))
|
|
self.wait()
|
|
self.play(
|
|
Write(equals),
|
|
Write(f_neg_p),
|
|
)
|
|
self.wait(6)
|
|
self.play(
|
|
FadeOut(VGroup(path, equals, f_neg_p))
|
|
)
|
|
dot.clear_updaters()
|
|
self.add(fp_label, gp_label)
|
|
gp_label.set_background_stroke(width=0)
|
|
self.play(
|
|
dot.move_to, ORIGIN,
|
|
VFadeOut(fp_label),
|
|
VFadeIn(gp_label),
|
|
)
|
|
self.wait(4)
|
|
self.play(
|
|
dot.move_to, self.odd_func(self.get_start_point())
|
|
)
|
|
# Flip, 2 second for flip, 7 seconds after
|
|
path = self.get_antipodal_path()
|
|
path.apply_function(self.odd_func)
|
|
end_dot = Dot(color=RED)
|
|
end_dot.move_to(path[-1].point_from_proportion(1))
|
|
g_neg_p = TexMobject(
|
|
"g", "(", "-\\vec{\\textbf{p}}", ")"
|
|
)
|
|
g_neg_p[0].set_color(GREEN)
|
|
g_neg_p[2].set_color(RED)
|
|
g_neg_p.next_to(end_dot, UR, SMALL_BUFF)
|
|
reflection_line = DashedLine(
|
|
dot.get_center(), end_dot.get_center(),
|
|
stroke_width=0,
|
|
)
|
|
vector = Vector(dot.get_center())
|
|
|
|
self.play(ShowCreation(path, run_time=1))
|
|
self.wait()
|
|
self.play(
|
|
ShowCreationThenDestruction(reflection_line, run_time=2),
|
|
TransformFromCopy(dot, end_dot),
|
|
ReplacementTransform(
|
|
gp_label.deepcopy().clear_updaters(), g_neg_p
|
|
),
|
|
)
|
|
self.wait()
|
|
self.play(FadeIn(vector))
|
|
self.play(Rotate(vector, angle=PI, about_point=ORIGIN))
|
|
self.play(FadeOut(vector))
|
|
self.play(
|
|
FadeOut(end_dot),
|
|
FadeOut(g_neg_p),
|
|
FadeOut(path),
|
|
)
|
|
|
|
def show_equator(self):
|
|
dot = self.dot
|
|
point_mob = self.tracked_point
|
|
equator = self.get_lat_line()
|
|
flat_eq = equator.deepcopy().apply_function(self.odd_func)
|
|
equator.fade(1)
|
|
|
|
equator_start = equator[0].point_from_proportion(0)
|
|
|
|
# To address
|
|
self.play(
|
|
point_mob.move_to, equator_start,
|
|
dot.move_to, self.odd_func(equator_start)
|
|
)
|
|
dot.add_updater(lambda m: m.move_to(
|
|
self.odd_func(point_mob.get_center())
|
|
))
|
|
self.play(
|
|
ShowCreation(equator),
|
|
ShowCreation(flat_eq),
|
|
run_time=4,
|
|
)
|
|
for x in range(2):
|
|
self.play(
|
|
Rotate(point_mob, PI, about_point=ORIGIN, axis=OUT),
|
|
run_time=4
|
|
)
|
|
self.wait(3)
|
|
self.play(
|
|
FadeOut(self.dot),
|
|
FadeOut(self.gp_label),
|
|
)
|
|
|
|
self.equator = equator
|
|
self.flat_eq = flat_eq
|
|
|
|
def deform_towards_north_pole(self):
|
|
equator = self.equator
|
|
flat_eq = self.flat_eq
|
|
|
|
self.play(
|
|
UpdateFromAlphaFunc(
|
|
equator,
|
|
lambda m, a: m.become(self.get_lat_line(a * PI / 2)).set_stroke(width=0),
|
|
run_time=16
|
|
),
|
|
UpdateFromFunc(
|
|
flat_eq,
|
|
lambda m: m.become(
|
|
equator.deepcopy().apply_function(self.odd_func).set_stroke(
|
|
color=RED, width=3
|
|
)
|
|
)
|
|
)
|
|
)
|
|
self.wait()
|
|
#
|
|
|
|
def func(self, point):
|
|
x, y, z = point
|
|
return 0.5 * self.axes.coords_to_point(
|
|
2 * x + 0.5 * y + z,
|
|
2 * y - 0.5 * np.sin(PI * x) + z**2 + 1 - x,
|
|
)
|
|
|
|
def odd_func(self, point):
|
|
return (self.func(point) - self.func(-point)) / 2
|
|
|
|
def get_dot(self, point):
|
|
return Dot(self.func(point))
|
|
|
|
def modify_path(self, path):
|
|
path.apply_function(self.func)
|
|
return path
|
|
|
|
|
|
class RotationOfEquatorGraphInOuputSpace(FunctionGOutputSpace):
|
|
def construct(self):
|
|
self.add(self.axes)
|
|
equator = self.get_lat_line(0)
|
|
equator.remove(*equator[len(equator) // 2:])
|
|
|
|
flat_eq = equator.copy().apply_function(self.odd_func)
|
|
vector = Vector(flat_eq[0].point_from_proportion(0))
|
|
vector_copy = vector.copy().fade(0.5)
|
|
|
|
self.add(flat_eq)
|
|
self.add(flat_eq.copy())
|
|
self.wait()
|
|
self.play(FadeIn(vector))
|
|
self.add(vector_copy)
|
|
self.play(
|
|
Rotate(
|
|
VGroup(flat_eq, vector),
|
|
PI, about_point=ORIGIN, run_time=5
|
|
))
|
|
self.play(FadeOut(vector), FadeOut(vector_copy))
|
|
|
|
|
|
class WriteInputSpace(Scene):
|
|
def construct(self):
|
|
self.play(Write(TextMobject("Input space")))
|
|
self.wait()
|
|
|
|
|
|
class WriteOutputSpace(Scene):
|
|
def construct(self):
|
|
self.play(Write(TextMobject("Output space")))
|
|
self.wait()
|
|
|
|
|
|
class LineScene(Scene):
|
|
def construct(self):
|
|
self.add(DashedLine(5 * LEFT, 5 * RIGHT, color=WHITE))
|
|
|
|
|
|
class ShowFlash(Scene):
|
|
def construct(self):
|
|
dot = Dot(ORIGIN, color=YELLOW)
|
|
dot.set_stroke(width=0)
|
|
dot.set_fill(opacity=0)
|
|
self.play(Flash(dot, flash_radius=0.8, line_length=0.6, run_time=2))
|
|
self.wait()
|
|
|
|
|
|
class WaitForIt(Scene):
|
|
def construct(self):
|
|
words = TextMobject("Wait for it", "$\\dots$", arg_separator="")
|
|
words.scale(2)
|
|
self.add(words[0])
|
|
self.play(Write(words[1], run_time=3))
|
|
self.wait()
|
|
|
|
|
|
class DrawSphere(SpecialThreeDScene):
|
|
def construct(self):
|
|
sphere = self.get_sphere()
|
|
sphere.shift(IN)
|
|
question = TextMobject("What \\emph{is} a sphere?")
|
|
question.set_width(FRAME_WIDTH - 3)
|
|
question.to_edge(UP)
|
|
self.move_camera(phi=70 * DEGREES, run_time=0)
|
|
self.begin_ambient_camera_rotation()
|
|
self.add_fixed_in_frame_mobjects(question)
|
|
self.play(
|
|
Write(sphere),
|
|
FadeInFromDown(question)
|
|
)
|
|
self.wait(4)
|
|
|
|
|
|
class DivisionOfUnity(Scene):
|
|
def construct(self):
|
|
factor = 2
|
|
line = Line(factor * LEFT, factor * RIGHT)
|
|
lower_brace = Brace(line, DOWN)
|
|
lower_brace.add(lower_brace.get_text("1"))
|
|
v_lines = VGroup(*[
|
|
DashedLine(0.2 * UP, 0.2 * DOWN).shift(factor * v)
|
|
for v in [LEFT, 0.3 * LEFT, 0.1 * RIGHT, RIGHT]
|
|
])
|
|
upper_braces = VGroup(*[
|
|
Brace(VGroup(vl1, vl2), UP)
|
|
for vl1, vl2 in zip(v_lines[:-1], v_lines[1:])
|
|
])
|
|
colors = color_gradient([GREEN, BLUE], 3)
|
|
for i, color, brace in zip(it.count(1), colors, upper_braces):
|
|
label = brace.get_tex("x_%d^2" % i)
|
|
label.set_color(color)
|
|
brace.add(label)
|
|
|
|
self.add(line, lower_brace)
|
|
self.play(LaggedStartMap(
|
|
ShowCreation, v_lines[1:3],
|
|
lag_ratio=0.8,
|
|
run_time=1
|
|
))
|
|
self.play(LaggedStartMap(
|
|
GrowFromCenter, upper_braces
|
|
))
|
|
self.wait()
|
|
|
|
|
|
class ThreeDSpace(ThreeDScene):
|
|
def construct(self):
|
|
axes = ThreeDAxes()
|
|
self.add(axes)
|
|
self.set_camera_orientation(phi=70 * DEGREES, theta=-130 * DEGREES)
|
|
self.begin_ambient_camera_rotation()
|
|
|
|
density = 1
|
|
radius = 3
|
|
lines = VGroup(*[
|
|
VGroup(*[
|
|
Line(
|
|
radius * IN, radius * OUT,
|
|
stroke_color=WHITE,
|
|
stroke_width=1,
|
|
stroke_opacity=0.5,
|
|
).shift(x * RIGHT + y * UP)
|
|
for x in np.arange(-radius, radius + density, density)
|
|
for y in np.arange(-radius, radius + density, density)
|
|
]).rotate(n * 120 * DEGREES, axis=[1, 1, 1])
|
|
for n in range(3)
|
|
])
|
|
|
|
self.play(Write(lines))
|
|
self.wait(30)
|
|
|
|
|
|
class NecklaceSphereConnectionTitle(Scene):
|
|
def construct(self):
|
|
text = TextMobject("Necklace Sphere Association")
|
|
text.set_width(FRAME_WIDTH - 1)
|
|
self.add(text)
|
|
|
|
|
|
class BorsukEndScreen(PatreonEndScreen):
|
|
CONFIG = {
|
|
"specific_patrons": [
|
|
"Ali Yahya",
|
|
"Meshal Alshammari",
|
|
"Crypticswarm",
|
|
"Ankit Agarwal",
|
|
"Yu Jun",
|
|
"Shelby Doolittle",
|
|
"Dave Nicponski",
|
|
"Damion Kistler",
|
|
"Juan Benet",
|
|
"Othman Alikhan",
|
|
"Justin Helps",
|
|
"Markus Persson",
|
|
"Dan Buchoff",
|
|
"Derek Dai",
|
|
"Joseph John Cox",
|
|
"Luc Ritchie",
|
|
"Guido Gambardella",
|
|
"Jerry Ling",
|
|
"Mark Govea",
|
|
"Vecht ",
|
|
"Jonathan Eppele",
|
|
"Shimin Kuang",
|
|
"Rish Kundalia",
|
|
"Achille Brighton",
|
|
"Kirk Werklund",
|
|
"Ripta Pasay",
|
|
"Felipe Diniz",
|
|
],
|
|
"n_patron_columns": 2,
|
|
}
|
|
|
|
|
|
class Thumbnail(SpecialThreeDScene):
|
|
def construct(self):
|
|
sphere = ParametricSurface(
|
|
func=lambda u, v: 2 * np.array([
|
|
np.cos(v) * np.sin(u) + 0.2 * np.cos(3 * u),
|
|
np.sin(v) * np.sin(u),
|
|
np.cos(u) + 0.2 * np.sin(4 * v) - 0.3 * np.cos(3 * u)
|
|
]),
|
|
resolution=(24, 48),
|
|
u_min=0.001,
|
|
u_max=PI - 0.001,
|
|
v_min=0,
|
|
v_max=TAU,
|
|
)
|
|
sphere.rotate(70 * DEGREES, DOWN)
|
|
self.set_camera_orientation(
|
|
phi=80 * DEGREES,
|
|
theta=-90 * DEGREES,
|
|
)
|
|
# randy = Randolph(mode="telepath")
|
|
# eyes = VGroup(randy.eyes, randy.pupils)
|
|
# eyes.scale(3.5)
|
|
# eyes.rotate(90 * DEGREES, RIGHT)
|
|
# eyes.next_to(sphere, OUT, buff=0)
|
|
|
|
self.add(sphere)
|