mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +08:00
842 lines
24 KiB
Python
842 lines
24 KiB
Python
from manimlib.imports import *
|
|
import json
|
|
|
|
|
|
OUTPUT_DIRECTORY = "spirals"
|
|
INV_113_MOD_710 = 377 # Inverse of 113 mode 710
|
|
INV_7_MOD_44 = 19
|
|
|
|
|
|
def generate_prime_list(*args):
|
|
if len(args) == 1:
|
|
start, stop = 2, args[0]
|
|
elif len(args) == 2:
|
|
start, stop = args
|
|
start = max(start, 2)
|
|
else:
|
|
raise TypeError("generate_prime_list takes 1 or 2 arguments")
|
|
|
|
result = []
|
|
for n in range(start, stop):
|
|
include = True
|
|
for k in range(2, int(np.sqrt(n)) + 1):
|
|
if n % k == 0:
|
|
include = False
|
|
break
|
|
if include:
|
|
result.append(n)
|
|
return result
|
|
|
|
|
|
def get_gcd(x, y):
|
|
while y > 0:
|
|
x, y = y, x % y
|
|
return x
|
|
|
|
|
|
def read_in_primes(max_N=None):
|
|
with open(os.path.join("assets", "primes.json")) as fp:
|
|
primes = np.array(json.load(fp))
|
|
if max_N:
|
|
return primes[primes <= max_N]
|
|
return primes
|
|
|
|
|
|
class SpiralScene(MovingCameraScene):
|
|
CONFIG = {
|
|
"axes_config": {
|
|
"number_line_config": {
|
|
"stroke_width": 1.5,
|
|
}
|
|
},
|
|
"default_dot_color": TEAL,
|
|
"p_spiral_width": 6,
|
|
}
|
|
|
|
def setup(self):
|
|
super().setup()
|
|
self.axes = Axes(**self.axes_config)
|
|
self.add(self.axes)
|
|
|
|
def get_v_spiral(self, sequence, axes=None, data_point_width=None):
|
|
if axes is None:
|
|
axes = self.axes
|
|
if data_point_width is None:
|
|
unit = get_norm(axes.c2p(1, 0) - axes.c2p(0, 0)),
|
|
data_point_width = max(
|
|
0.2 / (-np.log10(unit) + 1),
|
|
0.02,
|
|
)
|
|
|
|
return VGroup(*[
|
|
Square(
|
|
side_length=data_point_width,
|
|
fill_color=self.default_dot_color,
|
|
fill_opacity=1,
|
|
stroke_width=0,
|
|
).move_to(self.get_polar_point(n, n, axes))
|
|
for n in sequence
|
|
])
|
|
|
|
def get_p_spiral(self, sequence, axes=None):
|
|
if axes is None:
|
|
axes = self.axes
|
|
result = PMobject(
|
|
color=self.default_dot_color,
|
|
stroke_width=self.p_spiral_width,
|
|
)
|
|
result.add_points([
|
|
self.get_polar_point(n, n, axes)
|
|
for n in sequence
|
|
])
|
|
return result
|
|
|
|
def get_prime_v_spiral(self, max_N, **kwargs):
|
|
primes = read_in_primes(max_N)
|
|
return self.get_v_spiral(primes, **kwargs)
|
|
|
|
def get_prime_p_spiral(self, max_N, **kwargs):
|
|
primes = read_in_primes(max_N)
|
|
return self.get_p_spiral(primes, **kwargs)
|
|
|
|
def get_polar_point(self, r, theta, axes=None):
|
|
if axes is None:
|
|
axes = self.axes
|
|
return axes.c2p(r * np.cos(theta), r * np.sin(theta))
|
|
|
|
def set_scale(self, scale,
|
|
axes=None,
|
|
spiral=None,
|
|
to_shrink=None,
|
|
min_data_point_width=0.05,
|
|
target_p_spiral_width=None,
|
|
run_time=3):
|
|
if axes is None:
|
|
axes = self.axes
|
|
sf = self.get_scale_factor(scale, axes)
|
|
|
|
anims = []
|
|
for mob in [axes, spiral, to_shrink]:
|
|
if mob is None:
|
|
continue
|
|
mob.generate_target()
|
|
mob.target.scale(sf, about_point=ORIGIN)
|
|
if mob is spiral:
|
|
if isinstance(mob, VMobject):
|
|
old_width = mob[0].get_width()
|
|
for submob in mob.target:
|
|
submob.set_width(max(
|
|
old_width * sf,
|
|
min_data_point_width,
|
|
))
|
|
elif isinstance(mob, PMobject):
|
|
if target_p_spiral_width is not None:
|
|
mob.target.set_stroke_width(target_p_spiral_width)
|
|
anims.append(MoveToTarget(mob))
|
|
|
|
if run_time == 0:
|
|
for anim in anims:
|
|
anim.begin()
|
|
anim.update(1)
|
|
anim.finish()
|
|
else:
|
|
self.play(
|
|
*anims,
|
|
run_time=run_time,
|
|
rate_func=lambda t: interpolate(
|
|
smooth(t),
|
|
smooth(t)**(sf**(0.5)),
|
|
t,
|
|
)
|
|
)
|
|
|
|
def get_scale_factor(self, target_scale, axes=None):
|
|
if axes is None:
|
|
axes = self.axes
|
|
unit = get_norm(axes.c2p(1, 0) - axes.c2p(0, 0))
|
|
return 1 / (target_scale * unit)
|
|
|
|
|
|
# Scenes
|
|
|
|
|
|
class RefresherOnPolarCoordinates(MovingCameraScene):
|
|
CONFIG = {
|
|
"x_color": GREEN,
|
|
"y_color": RED,
|
|
"r_color": YELLOW,
|
|
"theta_color": LIGHT_PINK,
|
|
}
|
|
|
|
def construct(self):
|
|
self.show_xy_coordinates()
|
|
self.transition_to_polar_grid()
|
|
self.show_polar_coordinates()
|
|
|
|
self.show_all_nn_tuples()
|
|
|
|
def show_xy_coordinates(self):
|
|
plane = NumberPlane()
|
|
plane.add_coordinates()
|
|
|
|
x = 3 * np.cos(PI / 6)
|
|
y = 3 * np.sin(PI / 6)
|
|
|
|
point = plane.c2p(x, y)
|
|
xp = plane.c2p(x, 0)
|
|
origin = plane.c2p(0, 0)
|
|
|
|
x_color = self.x_color
|
|
y_color = self.y_color
|
|
|
|
x_line = Line(origin, xp, color=x_color)
|
|
y_line = Line(xp, point, color=y_color)
|
|
|
|
dot = Dot(point)
|
|
|
|
coord_label = self.get_coord_label(0, 0, x_color, y_color)
|
|
x_coord = coord_label.x_coord
|
|
y_coord = coord_label.y_coord
|
|
|
|
coord_label.next_to(dot, UR, SMALL_BUFF)
|
|
|
|
x_brace = Brace(x_coord, UP)
|
|
y_brace = Brace(y_coord, UP)
|
|
x_brace.add(x_brace.get_tex("x").set_color(x_color))
|
|
y_brace.add(y_brace.get_tex("y").set_color(y_color))
|
|
x_brace.add_updater(lambda m: m.next_to(x_coord, UP, SMALL_BUFF))
|
|
y_brace.add_updater(lambda m: m.next_to(y_coord, UP, SMALL_BUFF))
|
|
|
|
self.add(plane)
|
|
self.add(dot, coord_label)
|
|
self.add(x_brace, y_brace)
|
|
|
|
coord_label.add_updater(
|
|
lambda m: m.next_to(dot, UR, SMALL_BUFF)
|
|
)
|
|
|
|
self.play(
|
|
ShowCreation(x_line),
|
|
ChangeDecimalToValue(x_coord, x),
|
|
UpdateFromFunc(
|
|
dot,
|
|
lambda d: d.move_to(x_line.get_end()),
|
|
),
|
|
run_time=2,
|
|
)
|
|
self.play(
|
|
ShowCreation(y_line),
|
|
ChangeDecimalToValue(y_coord, y),
|
|
UpdateFromFunc(
|
|
dot,
|
|
lambda d: d.move_to(y_line.get_end()),
|
|
),
|
|
run_time=2,
|
|
)
|
|
self.wait()
|
|
|
|
self.xy_coord_mobjects = VGroup(
|
|
x_line, y_line, coord_label,
|
|
x_brace, y_brace,
|
|
)
|
|
self.plane = plane
|
|
self.dot = dot
|
|
|
|
def transition_to_polar_grid(self):
|
|
self.polar_grid = self.get_polar_grid()
|
|
self.add(self.polar_grid, self.dot)
|
|
self.play(
|
|
FadeOut(self.xy_coord_mobjects),
|
|
FadeOut(self.plane),
|
|
ShowCreation(self.polar_grid, run_time=2),
|
|
)
|
|
self.wait()
|
|
|
|
def show_polar_coordinates(self):
|
|
dot = self.dot
|
|
plane = self.plane
|
|
origin = plane.c2p(0, 0)
|
|
|
|
r_color = self.r_color
|
|
theta_color = self.theta_color
|
|
|
|
r_line = Line(origin, dot.get_center())
|
|
r_line.set_color(r_color)
|
|
r_value = r_line.get_length()
|
|
theta_value = r_line.get_angle()
|
|
|
|
coord_label = self.get_coord_label(r_value, theta_value, r_color, theta_color)
|
|
r_coord = coord_label.x_coord
|
|
theta_coord = coord_label.y_coord
|
|
|
|
coord_label.add_updater(lambda m: m.next_to(dot, UP, buff=SMALL_BUFF))
|
|
r_coord.add_updater(lambda d: d.set_value(
|
|
get_norm(dot.get_center())
|
|
))
|
|
theta_coord.add_background_rectangle()
|
|
theta_coord.add_updater(lambda d: d.set_value(
|
|
(angle_of_vector(dot.get_center()) % TAU)
|
|
))
|
|
coord_label[-1].add_updater(
|
|
lambda m: m.next_to(theta_coord, RIGHT, SMALL_BUFF)
|
|
)
|
|
|
|
non_coord_parts = VGroup(*[
|
|
part
|
|
for part in coord_label
|
|
if part not in [r_coord, theta_coord]
|
|
])
|
|
|
|
r_label = TexMobject("r")
|
|
r_label.set_color(r_color)
|
|
r_label.add_updater(lambda m: m.next_to(r_coord, UP))
|
|
theta_label = TexMobject("\\theta")
|
|
theta_label.set_color(theta_color)
|
|
theta_label.add_updater(lambda m: m.next_to(theta_coord, UP))
|
|
|
|
r_coord_copy = r_coord.copy()
|
|
r_coord_copy.add_updater(
|
|
lambda m: m.next_to(r_line.get_center(), UL, buff=0)
|
|
)
|
|
|
|
degree_label = DecimalNumber(0, num_decimal_places=1, unit="^\\circ")
|
|
arc = Arc(radius=1, angle=theta_value)
|
|
arc.set_color(theta_color)
|
|
degree_label.set_color(theta_color)
|
|
|
|
# Show r
|
|
self.play(
|
|
ShowCreation(r_line, run_time=2),
|
|
ChangeDecimalToValue(r_coord_copy, r_value, run_time=2),
|
|
VFadeIn(r_coord_copy, run_time=0.5),
|
|
)
|
|
r_coord.set_value(r_value)
|
|
self.add(non_coord_parts, r_coord_copy)
|
|
self.play(
|
|
FadeIn(non_coord_parts),
|
|
ReplacementTransform(r_coord_copy, r_coord),
|
|
FadeInFromDown(r_label),
|
|
)
|
|
self.wait()
|
|
|
|
# Show theta
|
|
degree_label.next_to(arc.get_start(), UR, SMALL_BUFF)
|
|
line = r_line.copy()
|
|
line.rotate(-theta_value, about_point=ORIGIN)
|
|
line.set_color(theta_color)
|
|
self.play(
|
|
ShowCreation(arc),
|
|
Rotate(line, theta_value, about_point=ORIGIN),
|
|
VFadeInThenOut(line),
|
|
ChangeDecimalToValue(degree_label, theta_value / DEGREES),
|
|
)
|
|
self.play(
|
|
degree_label.scale, 0.9,
|
|
degree_label.move_to, theta_coord,
|
|
FadeInFromDown(theta_label),
|
|
)
|
|
self.wait()
|
|
|
|
degree_cross = Cross(degree_label)
|
|
radians_word = TextMobject("in radians")
|
|
radians_word.scale(0.9)
|
|
radians_word.set_color(theta_color)
|
|
radians_word.add_background_rectangle()
|
|
radians_word.add_updater(
|
|
lambda m: m.next_to(theta_label, RIGHT, aligned_edge=DOWN)
|
|
)
|
|
|
|
self.play(ShowCreation(degree_cross))
|
|
self.play(
|
|
FadeOutAndShift(
|
|
VGroup(degree_label, degree_cross),
|
|
DOWN
|
|
),
|
|
FadeIn(theta_coord)
|
|
)
|
|
self.play(FadeIn(radians_word))
|
|
self.wait()
|
|
|
|
# Move point around
|
|
r_line.add_updater(
|
|
lambda l: l.put_start_and_end_on(ORIGIN, dot.get_center())
|
|
)
|
|
arc.add_updater(
|
|
lambda m: m.become(Arc(
|
|
angle=(r_line.get_angle() % TAU),
|
|
color=theta_color,
|
|
radius=1,
|
|
))
|
|
)
|
|
|
|
self.add(coord_label)
|
|
for angle in [PI - theta_value, PI - 0.001, -TAU + 0.002]:
|
|
self.play(
|
|
Rotate(dot, angle, about_point=ORIGIN),
|
|
run_time=3,
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
FadeOut(coord_label),
|
|
FadeOut(r_line),
|
|
FadeOut(arc),
|
|
FadeOut(r_label),
|
|
FadeOut(theta_label),
|
|
FadeOut(radians_word),
|
|
FadeOut(dot),
|
|
)
|
|
|
|
def show_all_nn_tuples(self):
|
|
self.remove(self.dot)
|
|
primes = generate_prime_list(20)
|
|
non_primes = list(range(1, 20))
|
|
for prime in primes:
|
|
non_primes.remove(prime)
|
|
|
|
pp_points = VGroup(*map(self.get_nn_point, primes))
|
|
pp_points[0][1].shift(0.3 * LEFT + SMALL_BUFF * UP)
|
|
np_points = VGroup(*map(self.get_nn_point, non_primes))
|
|
pp_points.set_color(TEAL)
|
|
np_points.set_color(WHITE)
|
|
pp_points.set_stroke(BLACK, 4, background=True)
|
|
np_points.set_stroke(BLACK, 4, background=True)
|
|
|
|
frame = self.camera_frame
|
|
self.play(
|
|
ApplyMethod(frame.scale, 2),
|
|
LaggedStartMap(
|
|
FadeInFromDown, pp_points
|
|
),
|
|
run_time=2
|
|
)
|
|
self.wait()
|
|
self.play(LaggedStartMap(FadeIn, np_points))
|
|
self.play(frame.scale, 0.5)
|
|
self.wait()
|
|
|
|
# Talk about 1
|
|
one = np_points[0]
|
|
r_line = Line(ORIGIN, one.dot.get_center())
|
|
r_line.set_color(self.r_color)
|
|
# pre_arc = Line(RIGHT, UR, color=self.r_color)
|
|
theta_tracker = ValueTracker(1)
|
|
arc = always_redraw(lambda: self.get_arc(theta_tracker.get_value()))
|
|
|
|
one_rect = SurroundingRectangle(one)
|
|
one_r_rect = SurroundingRectangle(one.label[1])
|
|
one_theta_rect = SurroundingRectangle(one.label[3])
|
|
one_theta_rect.set_color(self.theta_color)
|
|
|
|
self.play(ShowCreation(one_rect))
|
|
self.add(r_line, np_points, pp_points, one_rect)
|
|
self.play(
|
|
ReplacementTransform(one_rect, one_r_rect),
|
|
ShowCreation(r_line)
|
|
)
|
|
self.wait()
|
|
# self.play(TransformFromCopy(r_line, pre_arc))
|
|
# self.add(pre_arc, one)
|
|
self.play(
|
|
TransformFromCopy(r_line, arc),
|
|
ReplacementTransform(one_r_rect, one_theta_rect)
|
|
)
|
|
self.add(arc, one, one_theta_rect)
|
|
self.play(FadeOut(one_theta_rect))
|
|
self.wait()
|
|
|
|
# Talk about 2
|
|
self.play(theta_tracker.set_value, 2)
|
|
self.wait()
|
|
self.play(Rotate(r_line, angle=1, about_point=ORIGIN))
|
|
self.play(r_line.scale, 2, {'about_point': ORIGIN})
|
|
self.wait()
|
|
|
|
# And now 3
|
|
self.play(
|
|
theta_tracker.set_value, 3,
|
|
Rotate(r_line, angle=1, about_point=ORIGIN),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
r_line.scale, 3 / 2, {"about_point": ORIGIN}
|
|
)
|
|
self.wait()
|
|
|
|
# Finally 4
|
|
self.play(
|
|
theta_tracker.set_value, 4,
|
|
Rotate(r_line, angle=1, about_point=ORIGIN),
|
|
)
|
|
self.wait()
|
|
self.play(
|
|
r_line.scale, 4 / 3, {"about_point": ORIGIN}
|
|
)
|
|
self.wait()
|
|
|
|
# Zoom out and show spiral
|
|
spiral = ParametricFunction(
|
|
lambda t: self.get_polar_point(t, t),
|
|
t_min=0,
|
|
t_max=25,
|
|
stroke_width=1.5,
|
|
)
|
|
|
|
self.add(spiral, pp_points, np_points)
|
|
|
|
self.polar_grid.generate_target()
|
|
for mob in self.polar_grid:
|
|
if not isinstance(mob[0], Integer):
|
|
mob.set_stroke(width=1)
|
|
|
|
self.play(
|
|
ApplyMethod(
|
|
frame.scale, 3,
|
|
run_time=5,
|
|
rate_func=lambda t: smooth(t, 2)
|
|
),
|
|
ShowCreation(
|
|
spiral,
|
|
run_time=6,
|
|
),
|
|
FadeOut(r_line),
|
|
FadeOut(arc),
|
|
MoveToTarget(self.polar_grid)
|
|
)
|
|
self.wait()
|
|
|
|
#
|
|
def get_nn_point(self, n):
|
|
point = self.get_polar_point(n, n)
|
|
dot = Dot(point)
|
|
coord_label = self.get_coord_label(
|
|
n, n,
|
|
include_background_rectangle=False,
|
|
num_decimal_places=0
|
|
)
|
|
coord_label.next_to(dot, UR, buff=0)
|
|
result = VGroup(dot, coord_label)
|
|
result.dot = dot
|
|
result.label = coord_label
|
|
return result
|
|
|
|
def get_polar_grid(self, radius=25):
|
|
plane = self.plane
|
|
axes = VGroup(
|
|
Line(radius * DOWN, radius * UP),
|
|
Line(radius * LEFT, radius * RIGHT),
|
|
)
|
|
axes.set_stroke(width=2)
|
|
circles = VGroup(*[
|
|
Circle(color=BLUE, stroke_width=1, radius=r)
|
|
for r in range(1, int(radius))
|
|
])
|
|
rays = VGroup(*[
|
|
Line(
|
|
ORIGIN, radius * RIGHT,
|
|
color=BLUE,
|
|
stroke_width=1,
|
|
).rotate(angle, about_point=ORIGIN)
|
|
for angle in np.arange(0, TAU, TAU / 16)
|
|
])
|
|
labels = VGroup(*[
|
|
Integer(n).scale(0.5).next_to(
|
|
plane.c2p(n, 0), DR, SMALL_BUFF
|
|
)
|
|
for n in range(1, int(radius))
|
|
])
|
|
|
|
return VGroup(
|
|
circles, rays, labels, axes,
|
|
)
|
|
|
|
def get_coord_label(self,
|
|
x=0,
|
|
y=0,
|
|
x_color=WHITE,
|
|
y_color=WHITE,
|
|
include_background_rectangle=True,
|
|
**decimal_kwargs):
|
|
x_coord = DecimalNumber(x, **decimal_kwargs)
|
|
x_coord.set_color(x_color)
|
|
y_coord = DecimalNumber(y, **decimal_kwargs)
|
|
y_coord.set_color(y_color)
|
|
|
|
coord_label = VGroup(
|
|
TexMobject("("), x_coord,
|
|
TexMobject(","), y_coord,
|
|
TexMobject(")")
|
|
)
|
|
coord_label.arrange(RIGHT, buff=SMALL_BUFF)
|
|
coord_label[2].align_to(coord_label[0], DOWN)
|
|
|
|
coord_label.x_coord = x_coord
|
|
coord_label.y_coord = y_coord
|
|
if include_background_rectangle:
|
|
coord_label.add_background_rectangle()
|
|
return coord_label
|
|
|
|
def get_polar_point(self, r, theta):
|
|
plane = self.plane
|
|
return plane.c2p(r * np.cos(theta), r * np.sin(theta))
|
|
|
|
def get_arc(self, theta, r=1, color=None):
|
|
if color is None:
|
|
color = self.theta_color
|
|
return Arc(
|
|
angle=theta,
|
|
radius=r,
|
|
stroke_color=color,
|
|
)
|
|
|
|
|
|
class IntroducePrimePatterns(SpiralScene):
|
|
CONFIG = {
|
|
"small_n_primes": 25000,
|
|
"big_n_primes": 1000000,
|
|
"axes_config": {
|
|
"x_min": -25,
|
|
"x_max": 25,
|
|
"y_min": -25,
|
|
"y_max": 25,
|
|
},
|
|
"spiral_scale": 3e3,
|
|
"ray_scale": 1e5,
|
|
}
|
|
|
|
def construct(self):
|
|
self.slowly_zoom_out()
|
|
self.show_clumps_of_four()
|
|
|
|
def slowly_zoom_out(self):
|
|
zoom_time = 8
|
|
|
|
prime_spiral = self.get_prime_p_spiral(self.small_n_primes)
|
|
prime_spiral.set_stroke_width(25)
|
|
self.add(prime_spiral)
|
|
|
|
self.set_scale(3, spiral=prime_spiral)
|
|
self.wait()
|
|
self.set_scale(
|
|
self.spiral_scale,
|
|
spiral=prime_spiral,
|
|
target_p_spiral_width=8,
|
|
run_time=zoom_time,
|
|
)
|
|
self.wait()
|
|
|
|
self.remove(prime_spiral)
|
|
prime_spiral = self.get_prime_p_spiral(self.big_n_primes)
|
|
prime_spiral.set_stroke_width(8)
|
|
self.set_scale(
|
|
self.ray_scale,
|
|
spiral=prime_spiral,
|
|
target_p_spiral_width=4,
|
|
run_time=zoom_time,
|
|
)
|
|
self.wait()
|
|
|
|
def show_clumps_of_four(self):
|
|
line_groups = VGroup()
|
|
for n in range(71):
|
|
group = VGroup()
|
|
for k in [-3, -1, 1, 3]:
|
|
r = ((10 * n + k) * INV_113_MOD_710) % 710
|
|
group.add(self.get_arithmetic_sequence_line(
|
|
710, r, self.big_n_primes
|
|
))
|
|
line_groups.add(group)
|
|
|
|
line_groups.set_stroke(YELLOW, 2, opacity=0.5)
|
|
|
|
self.play(ShowCreation(line_groups[0]))
|
|
for g1, g2 in zip(line_groups, line_groups[1:5]):
|
|
self.play(
|
|
FadeOut(g1),
|
|
ShowCreation(g2)
|
|
)
|
|
|
|
self.play(
|
|
FadeOut(line_groups[4]),
|
|
LaggedStartMap(
|
|
VFadeInThenOut,
|
|
line_groups[4:],
|
|
lag_ratio=0.5,
|
|
run_time=5,
|
|
)
|
|
)
|
|
self.wait()
|
|
|
|
def get_arithmetic_sequence_line(self, N, r, max_val, skip_factor=5):
|
|
line = VMobject()
|
|
line.set_points_smoothly([
|
|
self.get_polar_point(x, x)
|
|
for x in range(r, max_val, skip_factor * N)
|
|
])
|
|
return line
|
|
|
|
|
|
class AskWhat(TeacherStudentsScene):
|
|
def construct(self):
|
|
screen = self.screen
|
|
self.student_says(
|
|
"I'm sory,\\\\what?!?",
|
|
target_mode="angry",
|
|
look_at_arg=screen,
|
|
student_index=2,
|
|
added_anims=[
|
|
self.teacher.change, "happy", screen,
|
|
self.students[0].change, "confused", screen,
|
|
self.students[1].change, "confused", screen,
|
|
]
|
|
)
|
|
self.wait(3)
|
|
|
|
|
|
class CountSpirals(IntroducePrimePatterns):
|
|
CONFIG = {
|
|
"count_sound": "pen_click.wav",
|
|
}
|
|
|
|
def construct(self):
|
|
prime_spiral = self.get_prime_p_spiral(self.small_n_primes)
|
|
|
|
self.add(prime_spiral)
|
|
self.set_scale(
|
|
self.spiral_scale,
|
|
spiral=prime_spiral,
|
|
run_time=0,
|
|
)
|
|
|
|
spiral_lines = self.get_all_primative_arithmetic_lines(
|
|
44, self.small_n_primes, INV_7_MOD_44,
|
|
)
|
|
spiral_lines.set_stroke(YELLOW, 2, opacity=0.5)
|
|
|
|
counts = VGroup()
|
|
for n, spiral in zip(it.count(1), spiral_lines):
|
|
count = Integer(n)
|
|
count.move_to(spiral.point_from_proportion(0.25))
|
|
counts.add(count)
|
|
|
|
run_time = 3
|
|
self.play(
|
|
ShowIncreasingSubsets(spiral_lines),
|
|
ShowSubmobjectsOneByOne(counts),
|
|
run_time=run_time,
|
|
rate_func=linear,
|
|
)
|
|
self.add_count_clicks(len(spiral_lines), run_time)
|
|
self.play(
|
|
counts[-1].scale, 3,
|
|
counts[-1].set_stroke, BLACK, 5, {"background": True},
|
|
)
|
|
self.wait()
|
|
|
|
def get_all_primative_arithmetic_lines(self, N, max_val, mult_factor):
|
|
lines = VGroup()
|
|
for r in range(1, N):
|
|
if get_gcd(N, r) == 1:
|
|
lines.add(
|
|
self.get_arithmetic_sequence_line(N, (mult_factor * r) % N, max_val)
|
|
)
|
|
return lines
|
|
|
|
def add_count_clicks(self, N, time, rate_func=linear):
|
|
alphas = np.arange(0, 1, 1 / N)
|
|
if rate_func is linear:
|
|
delays = time * alphas
|
|
else:
|
|
delays = time * np.array([
|
|
binary_search(
|
|
rate_func,
|
|
alpha,
|
|
0,
|
|
1,
|
|
)
|
|
for alpha in alphas
|
|
])
|
|
for delay in delays:
|
|
self.add_sound(
|
|
self.count_sound,
|
|
time_offset=-delay,
|
|
gain=-15,
|
|
)
|
|
|
|
|
|
class CountRays(CountSpirals):
|
|
def construct(self):
|
|
prime_spiral = self.get_prime_p_spiral(self.big_n_primes)
|
|
|
|
self.add(prime_spiral)
|
|
self.set_scale(
|
|
self.ray_scale,
|
|
spiral=prime_spiral,
|
|
run_time=0,
|
|
)
|
|
|
|
spiral_lines = self.get_all_primative_arithmetic_lines(
|
|
710, self.big_n_primes, INV_113_MOD_710,
|
|
)
|
|
spiral_lines.set_stroke(YELLOW, 2, opacity=0.5)
|
|
|
|
counts = VGroup()
|
|
for n, spiral in zip(it.count(1), spiral_lines):
|
|
count = Integer(n)
|
|
count.move_to(spiral.point_from_proportion(0.25))
|
|
counts.add(count)
|
|
|
|
run_time = 6
|
|
self.play(
|
|
ShowIncreasingSubsets(spiral_lines),
|
|
ShowSubmobjectsOneByOne(counts),
|
|
run_time=run_time,
|
|
rate_func=smooth,
|
|
)
|
|
self.add_count_clicks(len(spiral_lines), run_time, rate_func=smooth)
|
|
self.play(
|
|
counts[-1].scale, 3,
|
|
counts[-1].set_stroke, BLACK, 5, {"background": True},
|
|
)
|
|
self.wait()
|
|
self.play(FadeOut(spiral_lines))
|
|
self.wait()
|
|
|
|
|
|
class AskAboutRelationToPrimes(TeacherStudentsScene):
|
|
def construct(self):
|
|
numbers = TextMobject("20, 280")
|
|
arrow = Arrow(LEFT, RIGHT)
|
|
primes = TextMobject("2, 3, 5, 7, 11, \\dots")
|
|
q_marks = TextMobject("???")
|
|
q_marks.set_color(YELLOW)
|
|
|
|
group = VGroup(numbers, arrow, primes)
|
|
group.arrange(RIGHT)
|
|
q_marks.next_to(arrow, UP)
|
|
group.add(q_marks)
|
|
group.scale(1.5)
|
|
group.next_to(self.pi_creatures, UP, LARGE_BUFF)
|
|
|
|
self.play(
|
|
self.get_student_changes(
|
|
*3 * ["maybe"],
|
|
look_at_arg=numbers,
|
|
),
|
|
self.teacher.change, "maybe", numbers,
|
|
ShowCreation(arrow),
|
|
FadeInFrom(numbers, RIGHT)
|
|
)
|
|
self.play(
|
|
FadeInFrom(primes, LEFT),
|
|
)
|
|
self.play(
|
|
LaggedStartMap(FadeInFromDown, q_marks[0]),
|
|
Blink(self.teacher)
|
|
)
|
|
self.wait(3)
|
|
|
|
|
|
class ZoomOutOnPrimesWithNumbers(IntroducePrimePatterns):
|
|
def construct(self):
|
|
pass
|