3b1b-videos/_2021/siggraph.py
2022-12-27 16:31:01 -08:00

239 lines
7.5 KiB
Python

from manim_imports_ext import *
class ThreePis(Scene):
def construct(self):
pis = VGroup(*(PiCreature() for x in range(3)))
for pi, color in zip(pis, (BLUE_E, BLUE_C, BLUE_D)):
pi.set_color(color)
pis.set_height(2)
pis.arrange(RIGHT, buff=2)
pis.to_corner(DR)
pis[2].flip()
words = ["Choas", "Linear\nalgebra", "Quaternions"]
modes = ["speaking", "tease", "hooray"]
for pi, word, mode in zip(pis, words, modes):
bubble = pi.get_bubble(
word,
bubble_type=SpeechBubble,
height=3, width=3, direction=RIGHT
)
bubble.add(bubble.content)
bubble.next_to(pi, UL)
bubble.shift(RIGHT)
pi.change(mode, bubble.content)
self.add(pi, bubble)
self.wait()
class PendulumAxes(Scene):
def construct(self):
plane = NumberPlane(
(-2, 2),
(-2, 2),
height=8,
width=8,
background_line_style={
"stroke_color": WHITE,
}
)
theta1 = OldTex("\\theta_1").next_to(plane.c2p(2, 0), RIGHT, SMALL_BUFF)
theta2 = OldTex("\\theta_2").next_to(plane.c2p(0, 2), DR, SMALL_BUFF)
labels = VGroup(theta1, theta2)
x_labels = VGroup(OldTex("-\\pi"), OldTex("-{\\pi \\over 2}"), OldTex("{\\pi \\over 2}"), OldTex("\\pi"))
y_labels = VGroup()
for label, x in zip(x_labels, (-2, -1, 1, 2)):
label.scale(0.5)
label.set_stroke(BLACK, 5, background=True)
label.next_to(plane.c2p(x, 0), DL, SMALL_BUFF)
y_label = label.copy()
y_label.next_to(plane.c2p(0, x), DL, SMALL_BUFF)
y_labels.add(y_label)
self.add(plane)
self.add(labels)
self.add(x_labels, y_labels)
class InterpolatingOrientations(ThreeDScene):
matrix = True
def construct(self):
# Frame
frame = self.camera.frame
frame.set_height(5)
self.camera.light_source.move_to((-5, -10, 5))
# 3d model
color = "#539ac3"
logo = Cube(color=color)
logo.replace_submobject(0, TexturedSurface(logo[0], "acm_logo"))
logo.set_depth(0.5, stretch=True)
logo.rotate(-45 * DEGREES)
logo.set_height(2)
logo.set_opacity(0.8)
logo_template = logo.copy()
# Axes
axes = ThreeDAxes(
(-3, 3),
(-3, 3),
(-2, 2),
width=6,
height=6,
depth=4,
)
axes.set_stroke(width=1)
axes.apply_depth_test()
# Bases
bases = DotCloud(np.identity(3))
bases.set_color((RED, GREEN, BLUE))
bases.make_3d()
lines = VGroup(*(Line() for x in range(3)))
lines.set_submobject_colors_by_gradient(RED, GREEN, BLUE)
lines.set_stroke(width=2)
lines.insert_n_curves(20)
lines.apply_depth_test()
def update_lines(lines):
for line, point in zip(lines, bases.get_points()):
line.put_start_and_end_on(ORIGIN, point)
lines.add_updater(update_lines)
def get_matrix():
return bases.get_points().T
# Tie logo to matrix
logo = always_redraw(
lambda: logo_template.copy().apply_matrix(get_matrix()).sort(lambda p: -p[1])
)
# Show matrix/quaternion label
if self.matrix:
mat_mob = DecimalMatrix(
get_matrix(),
element_to_mobject_config={
"num_decimal_places": 2,
"edge_to_fix": RIGHT
}
)
mat_mob.to_corner(UL)
def update_mat_mob(mat_mob):
for entry, value in zip(mat_mob.get_entries(), get_matrix().flatten()):
entry.set_value(value)
mat_mob.fix_in_frame()
mat_mob.set_column_colors(RED, GREEN, BLUE)
return mat_mob
mat_mob.add_updater(update_mat_mob)
self.add(mat_mob)
else:
quat_tracker = ValueTracker([1, 0, 0, 0])
bases.add_updater(lambda m: m.set_points(
rotation_matrix_from_quaternion(quat_tracker.get_value()).T
))
kw = {"num_decimal_places": 3, "include_sign": True}
quat_label = VGroup(
VGroup(DecimalNumber(1, **kw)),
VGroup(DecimalNumber(0, **kw), OldTex("i")),
VGroup(DecimalNumber(0, **kw), OldTex("j")),
VGroup(DecimalNumber(0, **kw), OldTex("k")),
)
for mob in quat_label:
mob.arrange(RIGHT, buff=SMALL_BUFF)
quat_label.arrange(DOWN, aligned_edge=LEFT, buff=MED_SMALL_BUFF)
quat_label.to_corner(UL)
quat_label.shift(2 * RIGHT)
def update_quat_label(quat_label):
colors = [WHITE, RED, GREEN, BLUE]
for mob, value, color in zip(quat_label, quat_tracker.get_value(), colors):
mob[0].set_value(value)
mob.set_color(color)
quat_label.fix_in_frame()
return quat_label
quat_label.add_updater(update_quat_label)
self.add(quat_label)
# Show 3d scene
self.add(axes)
self.add(bases)
self.add(lines)
self.add(logo)
def move_to_matrix(matrix, run_time=5):
self.play(
bases.animate.set_points(np.transpose(matrix)),
run_time=run_time
)
self.wait()
def move_to_quaternion(quaternion, run_time=5):
self.play(
quat_tracker.animate.set_value(quaternion),
UpdateFromFunc(
quat_tracker,
lambda m: m.set_value(normalize(m.get_value()))
),
run_time=run_time
)
self.wait()
self.play(
ShowCreation(axes),
frame.animate.reorient(-20, 60), run_time=3
)
self.wait()
# Using the matrix to interpolate
if self.matrix:
move_to_matrix(z_to_vector(RIGHT))
move_to_matrix(z_to_vector(LEFT))
move_to_matrix(z_to_vector(DOWN + RIGHT))
move_to_matrix(z_to_vector(UP + LEFT + OUT))
else:
move_to_quaternion(quaternion_from_angle_axis(PI / 2, UP))
move_to_quaternion(quaternion_from_angle_axis(-PI / 2, UP))
move_to_quaternion(quaternion_from_angle_axis(PI / 2, UR))
move_to_quaternion(quaternion_from_angle_axis(-0.3 * PI, UR))
class InterpolatingOrientationsWithQuaternions(InterpolatingOrientations):
matrix = False
class FirstStepIsToCare(Scene):
def construct(self):
morty = Mortimer()
morty.to_corner(DR)
self.play(PiCreatureSays(morty, OldTexText("The first step\\\\is to care.")))
class NeverNeeded(Scene):
def construct(self):
formula = OldTex("\\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}")
formula.set_height(2)
formula.to_corner(UL)
self.add(formula)
morty = Mortimer()
morty.to_corner(DR)
self.play(PiCreatureSays(morty, OldTexText("Who actually\\\\uses it!"), target_mode="angry"))
class Thanks(Scene):
def construct(self):
morty = Mortimer()
morty.to_corner(DR)
self.play(PiCreatureSays(morty, OldTexText("Thanks"), target_mode="gracious"))
morty.look_at(OUT)