mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
1603 lines
51 KiB
Python
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",
|
|
]
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|