3b1b-manim/old_projects/putnam.py
2019-05-02 20:36:14 -07:00

1603 lines
51 KiB
Python

from manimlib.imports import *
class ShowExampleTest(ExternallyAnimatedScene):
pass
class IntroducePutnam(Scene):
CONFIG = {
"dont_animate" : False,
}
def construct(self):
title = TextMobject("Putnam Competition")
title.to_edge(UP, buff = MED_SMALL_BUFF)
title.set_color(BLUE)
six_hours = TextMobject("6", "hours")
three_hours = TextMobject("3", "hours")
for mob in six_hours, three_hours:
mob.next_to(title, DOWN, MED_LARGE_BUFF)
# mob.set_color(BLUE)
three_hours.shift(FRAME_X_RADIUS*LEFT/2)
three_hours_copy = three_hours.copy()
three_hours_copy.shift(FRAME_X_RADIUS*RIGHT)
question_groups = VGroup(*[
VGroup(*[
TextMobject("%s%d)"%(c, i))
for i in range(1, 7)
]).arrange(DOWN, buff = MED_LARGE_BUFF)
for c in ("A", "B")
]).arrange(RIGHT, buff = FRAME_X_RADIUS - MED_SMALL_BUFF)
question_groups.to_edge(LEFT)
question_groups.to_edge(DOWN, MED_LARGE_BUFF)
flat_questions = VGroup(*it.chain(*question_groups))
rects = VGroup()
for questions in question_groups:
rect = SurroundingRectangle(questions, buff = MED_SMALL_BUFF)
rect.set_stroke(WHITE, 2)
rect.stretch_to_fit_width(FRAME_X_RADIUS - 1)
rect.move_to(questions.get_left() + MED_SMALL_BUFF*LEFT, LEFT)
rects.add(rect)
out_of_tens = VGroup()
for question in flat_questions:
out_of_ten = TexMobject("/10")
out_of_ten.set_color(GREEN)
out_of_ten.move_to(question)
dist = rects[0].get_width() - 1.2
out_of_ten.shift(dist*RIGHT)
out_of_tens.add(out_of_ten)
out_of_120 = TexMobject("/120")
out_of_120.next_to(title, RIGHT, LARGE_BUFF)
out_of_120.set_color(GREEN)
out_of_120.generate_target()
out_of_120.target.to_edge(RIGHT, LARGE_BUFF)
median = TexMobject("2")
median.next_to(out_of_120.target, LEFT, SMALL_BUFF)
median.set_color(RED)
median.align_to(out_of_120[-1])
median_words = TextMobject("Typical median $\\rightarrow$")
median_words.next_to(median, LEFT)
difficulty_strings = [
"Pretty hard",
"Hard",
"Harder",
"Very hard",
"Ughhh",
"Can I go home?"
]
colors = color_gradient([YELLOW, RED], len(difficulty_strings))
difficulties = VGroup()
for i, s, color in zip(it.count(), difficulty_strings, colors):
for question_group in question_groups:
question = question_group[i]
text = TextMobject("\\dots %s \\dots"%s)
text.scale(0.7)
text.next_to(question, RIGHT)
text.set_color(color)
difficulties.add(text)
if self.dont_animate:
test = VGroup()
test.rect = rects[0]
test.questions = question_groups[0]
test.out_of_tens = VGroup(*out_of_tens[:6])
test.difficulties = VGroup(*difficulties[::2])
test.digest_mobject_attrs()
self.test = test
return
self.add(title)
self.play(Write(six_hours))
self.play(LaggedStartMap(
GrowFromCenter, flat_questions,
run_time = 3,
))
self.play(
ReplacementTransform(six_hours, three_hours),
ReplacementTransform(six_hours.copy(), three_hours_copy),
*list(map(ShowCreation, rects))
)
self.wait()
self.play(LaggedStartMap(
DrawBorderThenFill, out_of_tens,
run_time = 3,
stroke_color = YELLOW
))
self.wait()
self.play(ReplacementTransform(
out_of_tens.copy(), VGroup(out_of_120),
lag_ratio = 0.5,
run_time = 2,
))
self.wait()
self.play(
title.next_to, median_words.copy(), LEFT, LARGE_BUFF,
MoveToTarget(out_of_120),
Write(median_words)
)
self.play(Write(median))
for difficulty in difficulties:
self.play(FadeIn(difficulty))
self.wait()
class NatureOf5sAnd6s(TeacherStudentsScene):
CONFIG = {
"test_scale_val" : 0.65
}
def construct(self):
test = self.get_test()
self.students.fade(1)
self.play(
test.scale, self.test_scale_val,
test.to_corner, UP+LEFT,
FadeIn(self.teacher),
self.get_student_changes(
*["horrified"]*3,
look_at_arg = test
)
)
self.wait()
mover = VGroup(
test.questions[-1].copy(),
test.difficulties[-1].copy(),
)
mover.generate_target()
mover.target.scale(1./self.test_scale_val)
mover.target.next_to(
self.teacher.get_corner(UP+LEFT), UP,
)
new_words = TextMobject("\\dots Potentially very elegant \\dots")
new_words.set_color(GREEN)
new_words.set_height(mover.target[1].get_height())
new_words.next_to(mover.target[0], RIGHT, SMALL_BUFF)
self.play(
MoveToTarget(mover),
self.teacher.change, "raise_right_hand",
)
self.change_student_modes(*["pondering"]*3)
self.play(Transform(mover[1], new_words))
self.look_at((FRAME_X_RADIUS*RIGHT + FRAME_Y_RADIUS*UP)/2)
self.wait(4)
###
def get_test(self):
prev_scene = IntroducePutnam(dont_animate = True)
return prev_scene.test
class OtherVideoClips(Scene):
def construct(self):
rect = ScreenRectangle()
rect.set_height(6.5)
rect.center()
rect.to_edge(DOWN)
titles = list(map(TextMobject, [
"Essence of calculus, chapter 1",
"Pi hiding in prime regularities",
"How do cryptocurrencies work?"
]))
self.add(rect)
last_title = None
for title in titles:
title.to_edge(UP, buff = MED_SMALL_BUFF)
if last_title:
self.play(ReplacementTransform(last_title, title))
else:
self.play(FadeIn(title))
self.wait(3)
last_title = title
class IntroduceTetrahedron(ExternallyAnimatedScene):
pass
class IntroduceTetrahedronSupplement(Scene):
def construct(self):
title = TextMobject("4", "random$^*$ points on sphere")
title.set_color(YELLOW)
question = TextMobject("Probability that this tetrahedron \\\\ contains the sphere's center?")
question.next_to(title, DOWN, MED_LARGE_BUFF)
group = VGroup(title, question)
group.set_width(FRAME_WIDTH-1)
group.to_edge(DOWN)
for n in range(1, 4):
num = TextMobject(str(n))
num.replace(title[0], dim_to_match = 1)
num.set_color(YELLOW)
self.add(num)
self.wait(0.7)
self.remove(num)
self.add(title[0])
self.play(FadeIn(title[1], lag_ratio = 0.5))
self.wait(2)
self.play(Write(question))
self.wait(2)
class IntroduceTetrahedronFootnote(Scene):
def construct(self):
words = TextMobject("""
$^*$Chosen independently with a \\\\
uniform distribution on the sphere.
""")
words.to_corner(UP+LEFT)
self.add(words)
self.wait(2)
class HowDoYouStart(TeacherStudentsScene):
def construct(self):
self.student_says(
"How do you even start?",
target_mode = "raise_left_hand"
)
self.change_student_modes("confused", "raise_left_hand", "erm")
self.wait()
self.teacher_says("Try a simpler case.")
self.change_student_modes(*["thinking"]*3)
self.wait(2)
class TwoDCase(Scene):
CONFIG = {
"center" : ORIGIN,
"random_seed" : 4,
"radius" : 2.5,
"center_color" : BLUE,
"point_color" : YELLOW,
"positive_triangle_color" : BLUE,
"negative_triangle_color" : RED,
"triangle_fill_opacity" : 0.25,
"n_initial_random_choices" : 9,
"n_p3_random_moves" : 4,
}
def construct(self):
self.add_title()
self.add_circle()
self.choose_three_random_points()
self.simplify_further()
self.fix_two_points_in_place()
self.note_special_region()
self.draw_lines_through_center()
self.ask_about_probability_p3_lands_in_this_arc()
self.various_arc_sizes_for_p1_p2_placements()
self.ask_about_average_arc_size()
self.fix_p1_in_place()
self.overall_probability()
def add_title(self):
title = TextMobject("2D Case")
title.to_corner(UP+LEFT)
self.add(title)
self.set_variables_as_attrs(title)
def add_circle(self):
circle = Circle(radius = self.radius, color = WHITE)
center_dot = Dot(color = self.center_color).center()
radius = DashedLine(ORIGIN, circle.radius*RIGHT)
VGroup(circle, center_dot, radius).shift(self.center)
self.add(center_dot)
self.play(ShowCreation(radius))
self.play(
ShowCreation(circle),
Rotating(radius, angle = 2*np.pi, about_point = self.center),
rate_func = smooth,
run_time = 2,
)
self.play(ShowCreation(
radius,
rate_func = lambda t : smooth(1-t),
remover = True
))
self.wait()
self.set_variables_as_attrs(circle, center_dot)
def choose_three_random_points(self):
point_mobs = self.get_point_mobs()
point_labels = self.get_point_mob_labels()
triangle = self.get_triangle()
self.point_labels_update = self.get_labels_update(point_mobs, point_labels)
self.triangle_update = self.get_triangle_update(point_mobs, triangle)
self.update_animations = [
self.triangle_update,
self.point_labels_update,
]
for anim in self.update_animations:
anim.update(0)
question = TextMobject(
"Probability that \\\\ this triangle \\\\",
"contains the center", "?",
arg_separator = "",
)
question.set_color_by_tex("center", self.center_color)
question.scale(0.8)
question.to_corner(UP+RIGHT)
self.question = question
self.play(LaggedStartMap(DrawBorderThenFill, point_mobs))
self.play(FadeIn(triangle))
self.wait()
self.play(LaggedStartMap(Write, point_labels))
self.wait()
self.play(Write(question))
for x in range(self.n_initial_random_choices):
self.change_point_mobs_randomly()
self.wait()
angles = self.get_point_mob_angles()
target_angles = [5*np.pi/8, 7*np.pi/8, 0]
self.change_point_mobs([ta - a for a, ta in zip(angles, target_angles)])
self.wait()
def simplify_further(self):
morty = Mortimer().flip()
morty.scale(0.75)
morty.to_edge(DOWN)
morty.shift(3.5*LEFT)
bubble = SpeechBubble(
direction = RIGHT,
height = 3, width = 3
)
bubble.pin_to(morty)
bubble.to_edge(LEFT, SMALL_BUFF)
bubble.write("Simplify \\\\ more!")
self.play(FadeIn(morty))
self.play(
morty.change, "hooray",
ShowCreation(bubble),
Write(bubble.content)
)
self.play(Blink(morty))
self.wait()
self.play(
morty.change, "happy",
morty.fade, 1,
*list(map(FadeOut, [bubble, bubble.content]))
)
self.remove(morty)
def fix_two_points_in_place(self):
push_pins = VGroup()
for point_mob in self.point_mobs[:-1]:
push_pin = SVGMobject(file_name = "push_pin")
push_pin.set_height(0.5)
push_pin.move_to(point_mob.get_center(), DOWN)
line = Line(ORIGIN, UP)
line.set_stroke(WHITE, 2)
line.set_height(0.1)
line.move_to(push_pin, UP)
line.shift(0.3*SMALL_BUFF*(2*DOWN+LEFT))
push_pin.add(line)
push_pin.set_fill(LIGHT_GREY)
push_pin.save_state()
push_pin.shift(UP)
push_pin.fade(1)
push_pins.add(push_pin)
self.play(LaggedStartMap(
ApplyMethod, push_pins,
lambda mob : (mob.restore,)
))
self.add_foreground_mobjects(push_pins)
d_thetas = 2*np.pi*np.random.random(self.n_p3_random_moves)
for d_theta in d_thetas:
self.change_point_mobs([0, 0, d_theta])
self.wait()
self.set_variables_as_attrs(push_pins)
def note_special_region(self):
point_mobs = self.point_mobs
angles = self.get_point_mob_angles()
all_arcs = self.get_all_arcs()
arc = all_arcs[-1]
arc_lines = VGroup()
for angle in angles[:2]:
line = Line(LEFT, RIGHT).scale(SMALL_BUFF)
line.shift(self.radius*RIGHT)
line.rotate(angle + np.pi)
line.shift(self.center)
line.set_stroke(arc.get_color())
arc_lines.add(line)
self.play(ShowCreation(arc_lines))
self.change_point_mobs([0, 0, angles[0]+np.pi-angles[2]])
self.change_point_mobs(
[0, 0, arc.angle],
ShowCreation(arc, run_time = 2)
)
self.change_point_mobs([0, 0, np.pi/4 - angles[1]])
self.change_point_mobs([0, 0, 0.99*np.pi], run_time = 4)
self.wait()
self.set_variables_as_attrs(all_arcs, arc, arc_lines)
def draw_lines_through_center(self):
point_mobs = self.point_mobs
angles = self.get_point_mob_angles()
all_arcs = self.all_arcs
lines = self.get_center_lines()
self.add_foreground_mobjects(self.center_dot)
for line in lines:
self.play(ShowCreation(line))
self.play(FadeIn(all_arcs), Animation(point_mobs))
self.remove(self.circle)
self.wait()
self.play(
all_arcs.space_out_submobjects, 1.5,
Animation(point_mobs),
rate_func = there_and_back,
run_time = 1.5,
)
self.wait()
self.change_point_mobs(
[0, 0, np.mean(angles[:2])+np.pi-angles[2]]
)
self.wait()
for x in range(3):
self.change_point_mobs([0, 0, np.pi/2])
self.wait()
def ask_about_probability_p3_lands_in_this_arc(self):
arc = self.arc
arrow = Vector(LEFT, color = BLUE)
arrow.next_to(arc.get_center(), RIGHT, MED_LARGE_BUFF)
question = TextMobject("Probability of landing \\\\ in this arc?")
question.scale(0.8)
question.next_to(arrow, RIGHT)
question.shift_onto_screen()
question.shift(SMALL_BUFF*UP)
answer = TexMobject(
"{\\text{Length of arc}", "\\over",
"\\text{Circumference}}"
)
answer.set_color_by_tex("arc", BLUE)
answer.scale(0.8)
answer.next_to(arrow, RIGHT)
equals = TexMobject("=")
equals.rotate(np.pi/2)
equals.next_to(answer, UP, buff = 0.35)
self.play(FadeIn(question), GrowArrow(arrow))
self.have_p3_jump_around_randomly(15)
self.play(
question.next_to, answer, UP, LARGE_BUFF,
Write(equals),
FadeIn(answer)
)
self.have_p3_jump_around_randomly(4)
angles = self.get_point_mob_angles()
self.change_point_mobs(
[0, 0, 1.35*np.pi - angles[2]],
run_time = 0,
)
self.wait()
question.add(equals)
self.arc_prob_question = question
self.arc_prob = answer
self.arc_size_arrow = arrow
def various_arc_sizes_for_p1_p2_placements(self):
arc = self.arc
self.triangle.save_state()
self.play(*list(map(FadeOut, [
self.push_pins, self.triangle, self.arc_lines
])))
self.update_animations.remove(self.triangle_update)
self.update_animations += [
self.get_center_lines_update(self.point_mobs, self.center_lines),
self.get_arcs_update(self.all_arcs)
]
#90 degree angle
self.change_point_mobs_to_angles([np.pi/2, np.pi], run_time = 1)
elbow = VGroup(
Line(DOWN, DOWN+RIGHT),
Line(DOWN+RIGHT, RIGHT),
)
elbow.scale(0.25)
elbow.shift(self.center)
ninety_degrees = TexMobject("90^\\circ")
ninety_degrees.next_to(elbow, DOWN+RIGHT, buff = 0)
proportion = DecimalNumber(0.25)
proportion.set_color(self.center_color)
# proportion.next_to(arc.point_from_proportion(0.5), DOWN, MED_LARGE_BUFF)
proportion.next_to(self.arc_size_arrow, DOWN)
def proportion_update_func(alpha):
angles = self.get_point_mob_angles()
diff = abs(angles[1]-angles[0])/(2*np.pi)
return min(diff, 1-diff)
proportion_update = ChangingDecimal(proportion, proportion_update_func)
self.play(ShowCreation(elbow), FadeIn(ninety_degrees))
self.wait()
self.play(
ApplyMethod(
arc.rotate_in_place, np.pi/12,
rate_func = wiggle,
)
)
self.play(LaggedStartMap(FadeIn, proportion, run_time = 1))
self.wait()
#Non right angles
angle_pairs = [
(0.26*np.pi, 1.24*np.pi),
(0.73*np.pi, 0.78*np.pi),
(0.5*np.pi, np.pi),
]
self.update_animations.append(proportion_update)
for angle_pair in angle_pairs:
self.change_point_mobs_to_angles(
angle_pair,
VGroup(elbow, ninety_degrees).fade, 1,
)
self.remove(elbow, ninety_degrees)
self.wait()
self.set_variables_as_attrs(proportion, proportion_update)
def ask_about_average_arc_size(self):
proportion = self.proportion
brace = Brace(proportion, DOWN, buff = SMALL_BUFF)
average = brace.get_text("Average?", buff = SMALL_BUFF)
self.play(
GrowFromCenter(brace),
Write(average)
)
for x in range(6):
self.change_point_mobs_to_angles(
2*np.pi*np.random.random(2)
)
self.change_point_mobs_to_angles(
[1.2*np.pi, 0.3*np.pi]
)
self.wait()
self.set_variables_as_attrs(brace, average)
def fix_p1_in_place(self):
push_pin = self.push_pins[0]
P1, P2, P3 = point_mobs = self.point_mobs
self.change_point_mobs_to_angles([0.9*np.pi])
push_pin.move_to(P1.get_center(), DOWN)
push_pin.save_state()
push_pin.shift(UP)
push_pin.fade(1)
self.play(push_pin.restore)
for angle in [0.89999*np.pi, -0.09999*np.pi, 0.4*np.pi]:
self.change_point_mobs_to_angles(
[0.9*np.pi, angle],
run_time = 4,
)
self.play(FadeOut(self.average[-1]))
def overall_probability(self):
point_mobs = self.point_mobs
triangle = self.triangle
one_fourth = TexMobject("1/4")
one_fourth.set_color(BLUE)
one_fourth.next_to(self.question, DOWN)
self.triangle_update.update(1)
self.play(
FadeIn(triangle),
Animation(point_mobs)
)
self.update_animations.append(self.triangle_update)
self.have_p3_jump_around_randomly(8, wait_time = 0.25)
self.play(ReplacementTransform(
self.proportion.copy(), VGroup(one_fourth)
))
self.have_p3_jump_around_randomly(32, wait_time = 0.25)
#####
def get_point_mobs(self):
points = np.array([
self.center + rotate_vector(self.radius*RIGHT, theta)
for theta in 2*np.pi*np.random.random(3)
])
for index in 0, 1, 0:
if self.points_contain_center(points):
break
points[index] -= self.center
points[index] *= -1
points[index] += self.center
point_mobs = self.point_mobs = VGroup(*[
Dot().move_to(point) for point in points
])
point_mobs.set_color(self.point_color)
return point_mobs
def get_point_mob_labels(self):
point_labels = VGroup(*[
TexMobject("P_%d"%(i+1))
for i in range(len(self.point_mobs))
])
point_labels.set_color(self.point_mobs.get_color())
self.point_labels = point_labels
return point_labels
def get_triangle(self):
triangle = self.triangle = RegularPolygon(n = 3)
triangle.set_fill(WHITE, opacity = self.triangle_fill_opacity)
return triangle
def get_center_lines(self):
angles = self.get_point_mob_angles()
lines = VGroup()
for angle in angles[:2]:
line = DashedLine(
self.radius*RIGHT, self.radius*LEFT
)
line.rotate(angle)
line.shift(self.center)
line.set_color(self.point_color)
lines.add(line)
self.center_lines = lines
return lines
def get_labels_update(self, point_mobs, labels):
def update_labels(labels):
for point_mob, label in zip(point_mobs, labels):
label.move_to(point_mob)
vect = point_mob.get_center() - self.center
vect /= get_norm(vect)
label.shift(MED_LARGE_BUFF*vect)
return labels
return UpdateFromFunc(labels, update_labels)
def get_triangle_update(self, point_mobs, triangle):
def update_triangle(triangle):
points = [pm.get_center() for pm in point_mobs]
triangle.set_points_as_corners(points)
if self.points_contain_center(points):
triangle.set_color(self.positive_triangle_color)
else:
triangle.set_color(self.negative_triangle_color)
return triangle
return UpdateFromFunc(triangle, update_triangle)
def get_center_lines_update(self, point_mobs, center_lines):
def update_lines(center_lines):
for point_mob, line in zip(point_mobs, center_lines):
point = point_mob.get_center() - self.center
line.rotate_in_place(
angle_of_vector(point) - line.get_angle()
)
line.move_to(self.center)
return center_lines
return UpdateFromFunc(center_lines, update_lines)
def get_arcs_update(self, all_arcs):
def update_arcs(arcs):
new_arcs = self.get_all_arcs()
Transform(arcs, new_arcs).update(1)
return arcs
return UpdateFromFunc(all_arcs, update_arcs)
def get_all_arcs(self):
angles = self.get_point_mob_angles()
all_arcs = VGroup()
for da0, da1 in it.product(*[[0, np.pi]]*2):
arc_angle = (angles[1]+da1) - (angles[0]+da0)
arc_angle = (arc_angle+np.pi)%(2*np.pi)-np.pi
arc = Arc(
start_angle = angles[0]+da0,
angle = arc_angle,
radius = self.radius,
stroke_width = 5,
)
arc.shift(self.center)
all_arcs.add(arc)
all_arcs.set_color_by_gradient(RED, MAROON_B, PINK, BLUE)
self.all_arcs = all_arcs
return all_arcs
def points_contain_center(self, points):
p0, p1, p2 = points
v1 = p1 - p0
v2 = p2 - p0
c = self.center - p0
M = np.matrix([v1[:2], v2[:2]]).T
M_inv = np.linalg.inv(M)
coords = np.dot(M_inv, c[:2])
return np.all(coords > 0) and (np.sum(coords.flatten()) <= 1)
def get_point_mob_theta_change_anim(self, point_mob, d_theta):
curr_theta = angle_of_vector(point_mob.get_center() - self.center)
d_theta = (d_theta + np.pi)%(2*np.pi) - np.pi
new_theta = curr_theta + d_theta
def update_point(point_mob, alpha):
theta = interpolate(curr_theta, new_theta, alpha)
point_mob.move_to(self.center + self.radius*(
np.cos(theta)*RIGHT + np.sin(theta)*UP
))
return point_mob
return UpdateFromAlphaFunc(point_mob, update_point, run_time = 2)
def change_point_mobs(self, d_thetas, *added_anims, **kwargs):
anims = it.chain(
self.update_animations,
[
self.get_point_mob_theta_change_anim(pm, dt)
for pm, dt in zip(self.point_mobs, d_thetas)
],
added_anims
)
self.play(*anims, **kwargs)
for update in self.update_animations:
update.update(1)
def change_point_mobs_randomly(self, *added_anims, **kwargs):
d_thetas = 2*np.pi*np.random.random(len(self.point_mobs))
self.change_point_mobs(d_thetas, *added_anims, **kwargs)
def change_point_mobs_to_angles(self, target_angles, *added_anims, **kwargs):
angles = self.get_point_mob_angles()
n_added_targets = len(angles) - len(target_angles)
target_angles = list(target_angles) + list(angles[-n_added_targets:])
self.change_point_mobs(
[ta-a for a, ta in zip(angles, target_angles)],
*added_anims, **kwargs
)
def get_point_mob_angles(self):
point_mobs = self.point_mobs
points = [pm.get_center() - self.center for pm in point_mobs]
return np.array(list(map(angle_of_vector, points)))
def have_p3_jump_around_randomly(self, n_jumps, wait_time = 0.75, run_time = 0):
for x in range(n_jumps):
self.change_point_mobs(
[0, 0, 2*np.pi*random.random()],
run_time = run_time
)
self.wait(wait_time)
class FixThreePointsOnSphere(ExternallyAnimatedScene):
pass
class AddCenterLinesAndPlanesToSphere(ExternallyAnimatedScene):
pass
class AverageSizeOfSphericalTriangleSection(ExternallyAnimatedScene):
pass
class AverageSizeOfSphericalTriangleSectionSupplement(Scene):
def construct(self):
words = TextMobject(
"Average size of \\\\", "this section", "?",
arg_separator = ""
)
words.set_color_by_tex("section", GREEN)
words.set_width(FRAME_WIDTH - 1)
words.to_edge(DOWN)
self.play(Write(words))
self.wait(3)
class TryASurfaceIntegral(TeacherStudentsScene):
def construct(self):
self.student_says("Can you do \\\\ a surface integral?")
self.change_student_modes("confused", "raise_left_hand", "confused")
self.wait()
self.teacher_says(
"I mean...you can \\emph{try}",
target_mode = "sassy",
)
self.wait(2)
class RevisitTwoDCase(TwoDCase):
CONFIG = {
"random_seed" : 4,
"center" : 3*LEFT + 0.5*DOWN,
"radius" : 2,
"n_random_trials" : 200,
}
def construct(self):
self.force_skipping()
self.setup_circle()
self.show_probability()
self.add_lines_and_comment_on_them()
self.rewrite_random_procedure()
self.four_possibilities_for_coin_flips()
def setup_circle(self):
point_mobs = self.get_point_mobs()
point_labels = self.get_point_mob_labels()
triangle = self.get_triangle()
circle = Circle(radius = self.radius, color = WHITE)
center_dot = Dot(color = self.center_color)
VGroup(circle, center_dot).shift(self.center)
self.point_labels_update = self.get_labels_update(point_mobs, point_labels)
self.triangle_update = self.get_triangle_update(point_mobs, triangle)
self.update_animations = [
self.triangle_update,
self.point_labels_update,
]
for anim in self.update_animations:
anim.update(1)
self.add(
center_dot, circle, triangle,
point_mobs, point_labels
)
self.add_foreground_mobjects(center_dot)
self.set_variables_as_attrs(circle, center_dot)
def show_probability(self):
title = TexMobject(
"P(\\text{triangle contains the center})",
"=", "1/4"
)
title.to_edge(UP, buff = MED_SMALL_BUFF)
title.set_color_by_tex("1/4", BLUE)
four = title[-1][-1]
four_circle = Circle(color = YELLOW)
four_circle.replace(four, dim_to_match = 1)
four_circle.scale_in_place(1.2)
self.n_in = 0
self.n_out = 0
frac = TexMobject(
"{0", "\\over", "\\quad 0", "+", "0 \\quad}", "="
)
placeholders = frac.get_parts_by_tex("0")
positions = [ORIGIN, RIGHT, LEFT]
frac.next_to(self.circle, RIGHT, 1.5*LARGE_BUFF)
def place_random_triangles(n, wait_time):
for x in range(n):
self.change_point_mobs_randomly(run_time = 0)
contain_center = self.points_contain_center(
[pm.get_center() for pm in self.point_mobs]
)
if contain_center:
self.n_in += 1
else:
self.n_out += 1
nums = list(map(Integer, [self.n_in, self.n_in, self.n_out]))
VGroup(*nums[:2]).set_color(self.positive_triangle_color)
VGroup(*nums[2:]).set_color(self.negative_triangle_color)
for num, placeholder, position in zip(nums, placeholders, positions):
num.move_to(placeholder, position)
decimal = DecimalNumber(float(self.n_in)/(self.n_in + self.n_out))
decimal.next_to(frac, RIGHT, SMALL_BUFF)
self.add(decimal, *nums)
self.wait(wait_time)
self.remove(decimal, *nums)
return VGroup(decimal, *nums)
self.play(Write(title))
self.add(frac)
self.remove(*placeholders)
place_random_triangles(10, 0.25)
nums = place_random_triangles(self.n_random_trials, 0.05)
self.add(nums)
self.wait()
self.play(*list(map(FadeOut, [frac, nums, title])))
def add_lines_and_comment_on_them(self):
center_lines = self.get_center_lines()
center_lines.save_state()
center_line_shadows = center_lines.copy()
center_line_shadows.set_stroke(LIGHT_GREY, 2)
arcs = self.get_all_arcs()
center_lines.generate_target()
center_lines.target.to_edge(RIGHT, buff = LARGE_BUFF)
rect = SurroundingRectangle(center_lines.target, buff = MED_SMALL_BUFF)
rect.set_stroke(WHITE, 2)
words1 = TextMobject("Helpful new objects")
words2 = TextMobject("Reframe problem around these")
for words in words1, words2:
words.scale(0.8)
words.next_to(rect, UP)
words.shift_onto_screen()
self.play(LaggedStartMap(ShowCreation, center_lines, run_time = 1))
self.play(
LaggedStartMap(FadeIn, arcs, run_time = 1),
Animation(self.point_mobs),
)
self.wait()
self.add(center_line_shadows)
self.play(MoveToTarget(center_lines))
self.play(ShowCreation(rect), Write(words1))
self.wait(2)
self.play(ReplacementTransform(words1, words2))
self.wait(2)
self.play(
center_lines.restore,
center_lines.fade, 1,
*list(map(FadeOut, [
rect, words2, center_line_shadows,
self.triangle, arcs,
self.point_mobs,
self.point_labels,
]))
)
center_lines.restore()
self.remove(center_lines)
def rewrite_random_procedure(self):
point_mobs = self.point_mobs
center_lines = self.center_lines
random_procedure = TextMobject("Random procedure")
underline = Line(LEFT, RIGHT)
underline.stretch_to_fit_width(random_procedure.get_width())
underline.scale(1.1)
underline.next_to(random_procedure, DOWN)
group = VGroup(random_procedure, underline)
group.to_corner(UP+RIGHT)
words = VGroup(*list(map(TextMobject, [
"Choose 3 random points",
"Choose 2 random lines",
"Flip coin for each line \\\\ to get $P_1$ and $P_2$",
"Choose $P_3$ at random"
])))
words.scale(0.8)
words.arrange(DOWN, buff = MED_LARGE_BUFF)
words.next_to(underline, DOWN)
words[1].set_color(YELLOW)
point_label_groups = VGroup()
for point_mob, label in zip(self.point_mobs, self.point_labels):
group = VGroup(point_mob, label)
group.save_state()
group.move_to(words[0], LEFT)
group.fade(1)
point_label_groups.add(group)
self.point_label_groups = point_label_groups
cross = Cross(words[0])
cross.set_stroke(RED, 6)
self.center_lines_update = self.get_center_lines_update(
point_mobs, center_lines
)
self.update_animations.append(self.center_lines_update)
self.update_animations.remove(self.triangle_update)
#Choose random points
self.play(
Write(random_procedure),
ShowCreation(underline)
)
self.play(FadeIn(words[0]))
self.play(LaggedStartMap(
ApplyMethod, point_label_groups,
lambda mob : (mob.restore,),
))
self.play(
ShowCreation(cross),
point_label_groups.fade, 1,
)
self.wait()
#Choose two random lines
self.center_lines_update.update(1)
self.play(
FadeIn(words[1]),
LaggedStartMap(GrowFromCenter, center_lines)
)
for x in range(3):
self.change_point_mobs_randomly(run_time = 1)
self.change_point_mobs_to_angles([0.8*np.pi, 1.3*np.pi])
#Flip a coin for each line
def flip_point_label_back_and_forth(point_mob, label):
for x in range(6):
point_mob.rotate(np.pi, about_point = self.center)
self.point_labels_update.update(1)
self.wait(0.5)
self.wait(0.5)
def choose_p1_and_p2():
for group in point_label_groups[:2]:
group.set_fill(self.point_color, 1)
flip_point_label_back_and_forth(*group)
choose_p1_and_p2()
self.play(Write(words[2]))
#Seems convoluted
randy = Randolph().flip()
randy.scale(0.5)
randy.to_edge(DOWN)
randy.shift(2*RIGHT)
self.play(point_label_groups.fade, 1)
self.change_point_mobs_randomly(run_time = 1)
choose_p1_and_p2()
point_label_groups.fade(1)
self.change_point_mobs_randomly(FadeIn(randy))
self.play(
PiCreatureSays(
randy, "Seems \\\\ convoluted",
bubble_kwargs = {"height" : 2, "width" : 2},
target_mode = "confused"
)
)
choose_p1_and_p2()
self.play(
FadeOut(randy.bubble),
FadeOut(randy.bubble.content),
randy.change, "pondering",
)
self.play(Blink(randy))
self.play(FadeOut(randy))
#Choosing the third point
self.change_point_mobs([0, 0, -np.pi/2], run_time = 0)
p3_group = point_label_groups[2]
p3_group.save_state()
p3_group.move_to(words[3], LEFT)
self.play(Write(words[3], run_time = 1))
self.play(
p3_group.restore,
p3_group.set_fill, YELLOW, 1
)
self.wait()
self.play(Swap(*words[2:4]))
self.wait()
#Once the continuous randomness is handled
rect = SurroundingRectangle(VGroup(words[1], words[3]))
rect.set_stroke(WHITE, 2)
brace = Brace(words[2], DOWN)
brace_text = brace.get_text("4 equally likely outcomes")
brace_text.scale_in_place(0.8)
self.play(ShowCreation(rect))
self.play(GrowFromCenter(brace))
self.play(Write(brace_text))
self.wait()
self.random_procedure_words = words
def four_possibilities_for_coin_flips(self):
arcs = self.all_arcs
point_mobs = self.point_mobs
arc = arcs[-1]
point_label_groups = self.point_label_groups
arc_update = self.get_arcs_update(arcs)
arc_update.update(1)
self.update_animations.append(arc_update)
def second_arc_update_func(arcs):
VGroup(*arcs[:-1]).set_stroke(width = 0)
arcs[-1].set_stroke(PINK, 6)
return arcs
second_arc_update = UpdateFromFunc(arcs, second_arc_update_func)
second_arc_update.update(1)
self.update_animations.append(second_arc_update)
self.update_animations.append(Animation(point_label_groups))
def do_the_rounds():
for index in 0, 1, 0, 1:
point_mob = point_mobs[index]
point_mob.generate_target()
point_mob.target.rotate(
np.pi, about_point = self.center,
)
self.play(
MoveToTarget(point_mob),
*self.update_animations,
run_time = np.sqrt(2)/4 #Hacky reasons to be irrational
)
self.wait()
self.revert_to_original_skipping_status()
do_the_rounds()
self.triangle_update.update(1)
self.remove(arcs)
self.update_animations.remove(arc_update)
self.update_animations.remove(second_arc_update)
self.play(FadeIn(self.triangle))
self.wait()
self.update_animations.insert(0, self.triangle_update)
do_the_rounds()
self.wait()
self.change_point_mobs_randomly()
for x in range(2):
do_the_rounds()
class ThisIsWhereItGetsGood(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"This is where \\\\ things get good",
target_mode = "hooray"
)
self.change_student_modes(*["hooray"]*3)
self.wait(2)
class ContrastTwoRandomProcesses(TwoDCase):
CONFIG = {
"radius" : 1.5,
"random_seed" : 0,
}
def construct(self):
circle = Circle(color = WHITE, radius = self.radius)
point_mobs = self.get_point_mobs()
for point in point_mobs:
point.scale_in_place(1.5)
point.set_stroke(RED, 1)
labels = self.get_point_mob_labels()
self.get_labels_update(point_mobs, labels).update(1)
center_lines = self.get_center_lines()
point_label_groups = VGroup(*[
VGroup(*pair) for pair in zip(point_mobs, labels)
])
right_circles = VGroup(*[
VGroup(circle, *point_label_groups[:i+1]).copy()
for i in range(3)
])
left_circles = VGroup(
VGroup(circle, center_lines).copy(),
VGroup(
circle, center_lines,
point_label_groups[2]
).copy(),
VGroup(
circle, center_lines,
*point_label_groups[2::-1]
).copy(),
)
for circles in left_circles, right_circles:
circles.scale(0.5)
circles[0].to_edge(UP, buff = MED_LARGE_BUFF)
circles[2].to_edge(DOWN, buff = MED_LARGE_BUFF)
for c1, c2 in zip(circles, circles[1:]):
circles.add(Arrow(c1[0], c2[0], color = GREEN))
left_circles.shift(3*LEFT)
right_circles.shift(3*RIGHT)
vs = TextMobject("vs.")
self.show_creation_of_circle_group(left_circles)
self.play(Write(vs))
self.show_creation_of_circle_group(right_circles)
self.wait()
def show_creation_of_circle_group(self, group):
circles = group[:3]
arrows = group[3:]
self.play(
ShowCreation(circles[0][0]),
FadeIn(VGroup(*circles[0][1:])),
)
for c1, c2, arrow in zip(circles, circles[1:], arrows):
self.play(
GrowArrow(arrow),
ApplyMethod(
c1.copy().shift,
c2[0].get_center() - c1[0].get_center(),
remover = True
)
)
self.add(c2)
n = len(c2) - len(c1)
self.play(*list(map(GrowFromCenter, c2[-n:])))
class Rewrite3DRandomProcedure(Scene):
def construct(self):
random_procedure = TextMobject("Random procedure")
underline = Line(LEFT, RIGHT)
underline.stretch_to_fit_width(random_procedure.get_width())
underline.scale(1.1)
underline.next_to(random_procedure, DOWN)
group = VGroup(random_procedure, underline)
group.to_corner(UP+LEFT)
words = VGroup(*list(map(TextMobject, [
"Choose 4 random points",
"Choose 3 random lines",
"Choose $P_4$ at random",
"Flip coin for each line \\\\ to get $P_1$, $P_2$, $P_3$",
])))
words.scale(0.8)
words.arrange(DOWN, buff = MED_LARGE_BUFF)
words.next_to(underline, DOWN)
words[1].set_color(YELLOW)
cross = Cross(words[0])
cross.set_stroke(RED, 6)
self.play(
Write(random_procedure),
ShowCreation(underline)
)
self.play(FadeIn(words[0]))
self.play(ShowCreation(cross))
self.wait()
self.play(LaggedStartMap(FadeIn, words[1]))
self.play(LaggedStartMap(FadeIn, words[2]))
self.wait(2)
self.play(Write(words[3]))
self.wait(3)
class AntipodalViewOfThreeDCase(ExternallyAnimatedScene):
pass
class ThreeDAnswer(Scene):
def construct(self):
words = TextMobject(
"Probability that the tetrahedron contains center:",
"$\\frac{1}{8}$"
)
words.set_width(FRAME_WIDTH - 1)
words.to_edge(DOWN)
words[1].set_color(BLUE)
self.play(Write(words))
self.wait(2)
class FormalWriteupScreenCapture(ExternallyAnimatedScene):
pass
class Formality(TeacherStudentsScene):
def construct(self):
words = TextMobject(
"Write-up by Ralph Howard and Paul Sisson (link below)"
)
words.scale(0.7)
words.to_corner(UP+LEFT, buff = MED_SMALL_BUFF)
self.student_says(
"How would you \\\\ write that down?",
target_mode = "sassy"
)
self.change_student_modes("confused", "sassy", "erm")
self.wait()
self.play(
Write(words),
FadeOut(self.students[1].bubble),
FadeOut(self.students[1].bubble.content),
self.teacher.change, "raise_right_hand"
)
self.change_student_modes(
*["pondering"]*3,
look_at_arg = words
)
self.wait(8)
class ProblemSolvingTakeaways(Scene):
def construct(self):
title = TextMobject("Problem solving takeaways")
underline = Line(LEFT, RIGHT)
underline.set_width(title.get_width()*1.1)
underline.next_to(title, DOWN)
group = VGroup(title, underline)
group.to_corner(UP+LEFT)
points = VGroup(*[
TextMobject(string, alignment = "")
for string in [
"Ask a simpler version \\\\ of the question",
"Try reframing the question \\\\ around new constructs",
]
])
points[0].set_color(BLUE)
points[1].set_color(YELLOW)
points.arrange(
DOWN, buff = LARGE_BUFF,
aligned_edge = LEFT
)
points.next_to(group, DOWN, LARGE_BUFF)
self.play(Write(title), ShowCreation(underline))
self.wait()
for point in points:
self.play(Write(point))
self.wait(3)
class BrilliantPuzzle(PiCreatureScene):
CONFIG = {
"random_seed" : 2,
}
def construct(self):
students = self.students
tests = VGroup()
for student in students:
test = self.get_test()
test.move_to(0.75*student.get_center())
tests.add(test)
student.test = test
for i, student in enumerate(students):
student.right = students[(i+1)%len(students)]
student.left = students[(i-1)%len(students)]
arrows = VGroup()
for s1, s2 in adjacent_pairs(self.students):
arrow = Arrow(
s1.get_center(), s2.get_center(),
path_arc = np.pi/2,
buff = 0.8
)
arrow.tip.shift(SMALL_BUFF*arrow.get_vector())
arrow.tip.shift(-0.1*SMALL_BUFF*arrow.tip.get_center())
# arrow.shift(-MED_SMALL_BUFF*arrow.get_vector())
arrow.set_color(RED)
arrow.pointing_right = True
arrows.add(arrow)
s1.arrow = arrow
arrow.student = s1
title = TextMobject("Puzzle from Brilliant")
title.scale(0.75)
title.to_corner(UP+LEFT)
question = TextMobject("Expected number of \\\\ circled students?")
question.to_corner(UP+RIGHT)
self.remove(students)
self.play(Write(title))
self.play(LaggedStartMap(GrowFromCenter, students))
self.play(
LaggedStartMap(Write, tests),
LaggedStartMap(
ApplyMethod, students,
lambda m : (m.change, "horrified", m.test)
)
)
self.wait()
self.play(LaggedStartMap(
ApplyMethod, students,
lambda m : (m.change, "conniving")
))
self.play(LaggedStartMap(ShowCreation, arrows))
for x in range(2):
self.swap_arrows_randomly(arrows)
self.wait()
circles = self.circle_students()
self.play(Write(question))
for x in range(10):
self.swap_arrows_randomly(arrows, FadeOut(circles))
circles = self.circle_students()
self.wait()
####
def get_test(self):
lines = VGroup(*[Line(ORIGIN, 0.5*RIGHT) for x in range(6)])
lines.arrange(DOWN, buff = SMALL_BUFF)
rect = SurroundingRectangle(lines)
rect.set_stroke(WHITE)
lines.set_stroke(WHITE, 2)
test = VGroup(rect, lines)
test.set_height(0.5)
return test
def create_pi_creatures(self):
self.students = VGroup(*[
PiCreature(
color = random.choice([BLUE_C, BLUE_D, BLUE_E, GREY_BROWN])
).scale(0.25).move_to(3*vect)
for vect in compass_directions(8)
])
return self.students
def get_arrow_swap_anim(self, arrow):
arrow.generate_target()
if arrow.pointing_right:
target_color = GREEN
target_angle = np.pi - np.pi/4
else:
target_color = RED
target_angle = np.pi + np.pi/4
arrow.target.set_color(target_color)
arrow.target.rotate(
target_angle,
about_point = arrow.student.get_center()
)
arrow.pointing_right = not arrow.pointing_right
return MoveToTarget(arrow, path_arc = np.pi)
def swap_arrows_randomly(self, arrows, *added_anims):
anims = []
for arrow in arrows:
if random.choice([True, False]):
anims.append(self.get_arrow_swap_anim(arrow))
self.play(*anims + list(added_anims))
def circle_students(self):
circles = VGroup()
circled_students = list(self.students)
for student in self.students:
if student.arrow.pointing_right:
to_remove = student.right
else:
to_remove = student.left
if to_remove in circled_students:
circled_students.remove(to_remove)
for student in circled_students:
circle = Circle(color = YELLOW)
circle.set_height(1.2*student.get_height())
circle.move_to(student)
circles.add(circle)
self.play(ShowCreation(circle))
return circles
class ScrollThroughBrilliantCourses(ExternallyAnimatedScene):
pass
class BrilliantProbability(ExternallyAnimatedScene):
pass
class Promotion(PiCreatureScene):
CONFIG = {
"seconds_to_blink" : 5,
}
def construct(self):
url = TextMobject("https://brilliant.org/3b1b/")
url.to_corner(UP+LEFT)
rect = Rectangle(height = 9, width = 16)
rect.set_height(5.5)
rect.next_to(url, DOWN)
rect.to_edge(LEFT)
self.play(
Write(url),
self.pi_creature.change, "raise_right_hand"
)
self.play(ShowCreation(rect))
self.wait(2)
self.change_mode("thinking")
self.wait()
self.look_at(url)
self.wait(10)
self.change_mode("happy")
self.wait(10)
self.change_mode("raise_right_hand")
self.wait(10)
self.remove(rect)
self.play(
url.next_to, self.pi_creature, UP+LEFT
)
url_rect = SurroundingRectangle(url)
self.play(ShowCreation(url_rect))
self.play(FadeOut(url_rect))
self.wait(3)
class AddedPromoWords(Scene):
def construct(self):
words = TextMobject(
"First", "$2^8$", "vistors get",
"$(e^\\pi - \\pi)\\%$", "off"
)
words.set_width(FRAME_WIDTH - 1)
words.to_edge(DOWN)
words.set_color_by_tex("2^8", YELLOW)
words.set_color_by_tex("pi", PINK)
self.play(Write(words))
self.wait()
class PatreonThanks(PatreonEndScreen):
CONFIG = {
"specific_patrons" : [
"Randall Hunt",
"Burt Humburg",
"CrypticSwarm",
"Juan Benet",
"David Kedmey",
"Marcus Schiebold",
"Ali Yahya",
"Mayank M. Mehrotra",
"Lukas Biewald",
"Yana Chernobilsky",
"Kaustuv DeBiswas",
"Kathryn Schmiedicke",
"Yu Jun",
"Dave Nicponski",
"Damion Kistler",
"Jordan Scales",
"Markus Persson",
"Egor Gumenuk",
"Yoni Nazarathy",
"Ryan Atallah",
"Joseph John Cox",
"Luc Ritchie",
"James Park",
"Samantha D. Suplee",
"Delton",
"Thomas Tarler",
"Jake Alzapiedi",
"Jonathan Eppele",
"Taro Yoshioka",
"1stViewMaths",
"Jacob Magnuson",
"Mark Govea",
"Dagan Harrington",
"Clark Gaebel",
"Eric Chow",
"Mathias Jansson",
"David Clark",
"Michael Gardner",
"Erik Sundell",
"Awoo",
"Dr. David G. Stork",
"Tianyu Ge",
"Ted Suzman",
"Linh Tran",
"Andrew Busey",
"John Haley",
"Ankalagon",
"Eric Lavault",
"Boris Veselinovich",
"Julian Pulgarin",
"Jeff Linse",
"Cooper Jones",
"Ryan Dahl",
"Robert Teed",
"Jason Hise",
"Meshal Alshammari",
"Bernd Sing",
"Mustafa Mahdi",
"Mathew Bramson",
"Jerry Ling",
"Shimin Kuang",
"Rish Kundalia",
"Achille Brighton",
"Ripta Pasay",
]
}