mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
commit
1826072bce
4 changed files with 791 additions and 0 deletions
47
active_projects/eop/birthday.py
Normal file
47
active_projects/eop/birthday.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
from helpers import *
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.vectorized_mobject import *
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import *
|
||||||
|
from animation.simple_animations import *
|
||||||
|
from topics.geometry import *
|
||||||
|
from scene import Scene
|
||||||
|
from camera import *
|
||||||
|
from topics.number_line import *
|
||||||
|
from topics.three_dimensions import *
|
||||||
|
from topics.light import *
|
||||||
|
from topics.characters import *
|
||||||
|
from topics.numerals import *
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Birthday(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
sidelength = 6.0
|
||||||
|
corner = np.array([-sidelength/2,-sidelength/2,0])
|
||||||
|
nb_days_left = 365.0
|
||||||
|
toggle = False
|
||||||
|
|
||||||
|
def probability():
|
||||||
|
width = rect.get_width()
|
||||||
|
height = rect.get_height()
|
||||||
|
return width * height / sidelength**2
|
||||||
|
|
||||||
|
rect = Square().scale(sidelength/2)
|
||||||
|
|
||||||
|
while probability() > 0.5:
|
||||||
|
|
||||||
|
self.add(rect.copy())
|
||||||
|
nb_days_left -= 1
|
||||||
|
|
||||||
|
if toggle:
|
||||||
|
dim = 0
|
||||||
|
else:
|
||||||
|
dim = 1
|
||||||
|
|
||||||
|
rect.stretch_about_point(nb_days_left / 365, dim, corner)
|
||||||
|
|
||||||
|
toggle = not toggle
|
||||||
|
|
272
active_projects/eop/histograms.py
Normal file
272
active_projects/eop/histograms.py
Normal file
|
@ -0,0 +1,272 @@
|
||||||
|
from helpers import *
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.vectorized_mobject import *
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import *
|
||||||
|
from animation.simple_animations import *
|
||||||
|
from topics.geometry import *
|
||||||
|
from scene import Scene
|
||||||
|
from camera import *
|
||||||
|
from topics.number_line import *
|
||||||
|
from topics.three_dimensions import *
|
||||||
|
from topics.light import *
|
||||||
|
from topics.characters import *
|
||||||
|
from topics.numerals import *
|
||||||
|
from random import *
|
||||||
|
|
||||||
|
def text_range(start,stop,step):
|
||||||
|
numbers = np.arange(start,stop,step)
|
||||||
|
labels = []
|
||||||
|
for x in numbers:
|
||||||
|
labels.append(str(x))
|
||||||
|
return labels
|
||||||
|
|
||||||
|
|
||||||
|
class Histogram(VMobject):
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
"start_color" : RED,
|
||||||
|
"end_color" : BLUE,
|
||||||
|
"x_scale" : 1.0,
|
||||||
|
"y_scale" : 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, x_values, y_values, **kwargs):
|
||||||
|
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
|
# preliminaries
|
||||||
|
self.x_values = x_values
|
||||||
|
self.y_values = y_values
|
||||||
|
|
||||||
|
self.x_steps = x_values[1:] - x_values[:-1]
|
||||||
|
self.x_min = x_values[0] - self.x_steps[0] * 0.5
|
||||||
|
self.x_posts = (x_values[1:] + x_values[:-1]) * 0.5
|
||||||
|
self.x_max = x_values[-1] + self.x_steps[-1] * 0.5
|
||||||
|
self.x_posts = np.insert(self.x_posts,0,self.x_min)
|
||||||
|
self.x_posts = np.append(self.x_posts,self.x_max)
|
||||||
|
|
||||||
|
self.x_widths = self.x_posts[1:] - self.x_posts[:-1]
|
||||||
|
|
||||||
|
self.x_values_scaled = self.x_scale * x_values
|
||||||
|
self.x_steps_scaled = self.x_scale * self.x_steps
|
||||||
|
self.x_posts_scaled = self.x_scale * self.x_posts
|
||||||
|
self.x_min_scaled = self.x_scale * self.x_min
|
||||||
|
self.x_max_scaled = self.x_scale * self.x_max
|
||||||
|
self.x_widths_scaled = self.x_scale * self.x_widths
|
||||||
|
|
||||||
|
self.y_values_scaled = self.y_scale * self.y_values
|
||||||
|
|
||||||
|
VMobject.__init__(self, **kwargs)
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
|
||||||
|
previous_bar = ORIGIN
|
||||||
|
self.bars = []
|
||||||
|
outline_points = []
|
||||||
|
self.x_labels = text_range(self.x_values[0], self.x_max, self.x_steps[0])
|
||||||
|
|
||||||
|
for (i,x) in enumerate(self.x_values):
|
||||||
|
|
||||||
|
bar = Rectangle(
|
||||||
|
width = self.x_widths_scaled[i],
|
||||||
|
height = self.y_values_scaled[i],
|
||||||
|
)
|
||||||
|
t = float(x - self.x_values[0])/(self.x_values[-1] - self.x_values[0])
|
||||||
|
bar_color = interpolate_color(
|
||||||
|
self.start_color,
|
||||||
|
self.end_color,
|
||||||
|
t
|
||||||
|
)
|
||||||
|
bar.set_fill(color = bar_color, opacity = 1)
|
||||||
|
bar.set_stroke(width = 0)
|
||||||
|
bar.next_to(previous_bar,RIGHT,buff = 0, aligned_edge = DOWN)
|
||||||
|
|
||||||
|
self.add(bar)
|
||||||
|
self.bars.append(bar)
|
||||||
|
|
||||||
|
label = TextMobject(self.x_labels[i])
|
||||||
|
label.next_to(bar,DOWN)
|
||||||
|
self.add(label)
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
# start with the lower left
|
||||||
|
outline_points.append(bar.get_anchors()[-2])
|
||||||
|
|
||||||
|
# upper two points of each bar
|
||||||
|
outline_points.append(bar.get_anchors()[0])
|
||||||
|
outline_points.append(bar.get_anchors()[1])
|
||||||
|
|
||||||
|
previous_bar = bar
|
||||||
|
|
||||||
|
# close the outline
|
||||||
|
# lower right
|
||||||
|
outline_points.append(bar.get_anchors()[2])
|
||||||
|
# lower left
|
||||||
|
outline_points.append(outline_points[0])
|
||||||
|
|
||||||
|
self.outline = Polygon(*outline_points)
|
||||||
|
self.outline.set_stroke(color = WHITE)
|
||||||
|
self.add(self.outline)
|
||||||
|
|
||||||
|
def get_lower_left_point(self):
|
||||||
|
return self.bars[0].get_anchors()[-2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FlashThroughHistogram(Animation):
|
||||||
|
CONFIG = {
|
||||||
|
"cell_color" : WHITE,
|
||||||
|
"cell_opacity" : 0.8,
|
||||||
|
"hist_opacity" : 0.2
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, mobject, direction = "horizontal", mode = "random", **kwargs):
|
||||||
|
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
|
||||||
|
self.cell_height = mobject.y_scale
|
||||||
|
self.prototype_cell = Rectangle(
|
||||||
|
width = 1,
|
||||||
|
height = self.cell_height,
|
||||||
|
fill_color = self.cell_color,
|
||||||
|
fill_opacity = self.cell_opacity,
|
||||||
|
stroke_width = 0,
|
||||||
|
)
|
||||||
|
|
||||||
|
x_values = mobject.x_values
|
||||||
|
y_values = mobject.y_values
|
||||||
|
|
||||||
|
self.mode = mode
|
||||||
|
self.direction = direction
|
||||||
|
|
||||||
|
self.generate_cell_indices(x_values,y_values)
|
||||||
|
Animation.__init__(self,mobject,**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def generate_cell_indices(self,x_values,y_values):
|
||||||
|
|
||||||
|
self.cell_indices = []
|
||||||
|
for (i,x) in enumerate(x_values):
|
||||||
|
|
||||||
|
nb_cells = y_values[i]
|
||||||
|
for j in range(nb_cells):
|
||||||
|
self.cell_indices.append((i, j))
|
||||||
|
|
||||||
|
self.reordered_cell_indices = self.cell_indices
|
||||||
|
if self.mode == "random":
|
||||||
|
shuffle(self.reordered_cell_indices)
|
||||||
|
|
||||||
|
|
||||||
|
def cell_for_index(self,i,j):
|
||||||
|
|
||||||
|
if self.direction == "vertical":
|
||||||
|
width = self.mobject.x_scale
|
||||||
|
height = self.mobject.y_scale
|
||||||
|
x = (i + 0.5) * self.mobject.x_scale
|
||||||
|
y = (j + 0.5) * self.mobject.y_scale
|
||||||
|
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||||
|
|
||||||
|
elif self.direction == "horizontal":
|
||||||
|
width = self.mobject.x_scale / self.mobject.y_values[i]
|
||||||
|
height = self.mobject.y_scale * self.mobject.y_values[i]
|
||||||
|
x = i * self.mobject.x_scale + (j + 0.5) * width
|
||||||
|
y = height / 2
|
||||||
|
center = self.mobject.get_lower_left_point() + x * RIGHT + y * UP
|
||||||
|
|
||||||
|
cell = Rectangle(width = width, height = height)
|
||||||
|
cell.move_to(center)
|
||||||
|
return cell
|
||||||
|
|
||||||
|
|
||||||
|
def update_mobject(self,t):
|
||||||
|
|
||||||
|
if t == 0:
|
||||||
|
self.mobject.add(self.prototype_cell)
|
||||||
|
|
||||||
|
flash_nb = int(t * (len(self.cell_indices))) - 1
|
||||||
|
(i,j) = self.reordered_cell_indices[flash_nb]
|
||||||
|
cell = self.cell_for_index(i,j)
|
||||||
|
self.prototype_cell.width = cell.get_width()
|
||||||
|
self.prototype_cell.height = cell.get_height()
|
||||||
|
self.prototype_cell.generate_points()
|
||||||
|
self.prototype_cell.move_to(cell.get_center())
|
||||||
|
|
||||||
|
#if t == 1:
|
||||||
|
# self.mobject.remove(self.prototype_cell)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SampleScene(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
x_values = np.array([1,2,3,4,5])
|
||||||
|
y_values = np.array([4,3,5,2,3])
|
||||||
|
|
||||||
|
hist1 = Histogram(
|
||||||
|
x_values = x_values,
|
||||||
|
y_values = y_values,
|
||||||
|
x_scale = 0.5,
|
||||||
|
y_scale = 0.5,
|
||||||
|
).shift(1*DOWN)
|
||||||
|
self.add(hist1)
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
y_values2 = np.array([3,8,7,15,5])
|
||||||
|
|
||||||
|
hist2 = Histogram(
|
||||||
|
x_values = x_values,
|
||||||
|
y_values = y_values2,
|
||||||
|
x_scale = 0.5,
|
||||||
|
y_scale = 0.5,
|
||||||
|
x_labels = text_range(1,6,1),
|
||||||
|
)
|
||||||
|
|
||||||
|
v1 = hist1.get_lower_left_point()
|
||||||
|
v2 = hist2.get_lower_left_point()
|
||||||
|
hist2.shift(v1 - v2)
|
||||||
|
|
||||||
|
# self.play(
|
||||||
|
# ReplacementTransform(hist1,hist2)
|
||||||
|
# )
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
FlashThroughHistogram(
|
||||||
|
hist1,
|
||||||
|
direction = "horizontal",
|
||||||
|
mode = "linear",
|
||||||
|
run_time = 10,
|
||||||
|
rate_func = None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
207
active_projects/eop/pascal.py
Normal file
207
active_projects/eop/pascal.py
Normal file
|
@ -0,0 +1,207 @@
|
||||||
|
from helpers import *
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.vectorized_mobject import *
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import *
|
||||||
|
from animation.simple_animations import *
|
||||||
|
from topics.geometry import *
|
||||||
|
from scene import Scene
|
||||||
|
from camera import *
|
||||||
|
|
||||||
|
nb_levels = 50
|
||||||
|
|
||||||
|
dev_x_step = 2
|
||||||
|
dev_y_step = 5
|
||||||
|
|
||||||
|
def rainbow_color(alpha):
|
||||||
|
nb_colors = 100
|
||||||
|
rainbow = color_gradient([RED, ORANGE, YELLOW, GREEN, BLUE, PURPLE], nb_colors)
|
||||||
|
rainbow = np.append(rainbow,PURPLE)
|
||||||
|
index = int(alpha * nb_colors)
|
||||||
|
return rainbow[index]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PascalScene(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
unit_width = 0.25
|
||||||
|
top_height = 4.0
|
||||||
|
level_height = 2.0 * top_height / nb_levels
|
||||||
|
|
||||||
|
start_points = np.array([top_height * UP])
|
||||||
|
|
||||||
|
dev_start = start_points[0]
|
||||||
|
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
for n in range(nb_levels):
|
||||||
|
|
||||||
|
half_width = 0.5 * (n + 0.5) * unit_width
|
||||||
|
|
||||||
|
stop_points_left = start_points.copy()
|
||||||
|
stop_points_left[:,0] -= 0.5 * unit_width
|
||||||
|
stop_points_left[:,1] -= level_height
|
||||||
|
|
||||||
|
stop_points_right = start_points.copy()
|
||||||
|
stop_points_right[:,0] += 0.5 * unit_width
|
||||||
|
stop_points_right[:,1] -= level_height
|
||||||
|
|
||||||
|
for (p,q) in zip(start_points,stop_points_left):
|
||||||
|
alpha = np.abs((p[0]+q[0])/2) / half_width
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
line = Line(p,q, stroke_color = color)
|
||||||
|
self.add(line)
|
||||||
|
|
||||||
|
for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)):
|
||||||
|
alpha = np.abs((p[0]+q[0])/2) / half_width
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
line = Line(p,q, stroke_color = color)
|
||||||
|
self.add(line)
|
||||||
|
|
||||||
|
if (n + 1) % dev_y_step == 0 and n != 1:
|
||||||
|
j += dev_x_step
|
||||||
|
dev_stop = stop_points_left[j]
|
||||||
|
line = Line(dev_start,dev_stop,stroke_color = WHITE)
|
||||||
|
self.add(line)
|
||||||
|
dot = Dot(dev_stop, fill_color = WHITE)
|
||||||
|
self.add_foreground_mobject(dot)
|
||||||
|
dev_start = dev_stop
|
||||||
|
|
||||||
|
start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0)
|
||||||
|
|
||||||
|
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class RescaledPascalScene(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
half_width = 3.0
|
||||||
|
top_height = 4.0
|
||||||
|
level_height = 2.0 * top_height / nb_levels
|
||||||
|
|
||||||
|
start_points = np.array([top_height * UP])
|
||||||
|
left_edge = top_height * UP + half_width * LEFT
|
||||||
|
right_edge = top_height * UP + half_width * RIGHT
|
||||||
|
|
||||||
|
dev_start = start_points[0]
|
||||||
|
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
for n in range(nb_levels):
|
||||||
|
|
||||||
|
if n == 0:
|
||||||
|
start_points_left_shift = np.array([left_edge])
|
||||||
|
else:
|
||||||
|
start_points_left_shift = start_points[:-1]
|
||||||
|
start_points_left_shift = np.insert(start_points_left_shift,0,left_edge, axis = 0)
|
||||||
|
stop_points_left = 0.5 * (start_points + start_points_left_shift)
|
||||||
|
stop_points_left += level_height * DOWN
|
||||||
|
|
||||||
|
|
||||||
|
if n == 0:
|
||||||
|
start_points_right_shift = np.array([right_edge])
|
||||||
|
else:
|
||||||
|
start_points_right_shift = start_points[1:]
|
||||||
|
start_points_right_shift = np.append(start_points_right_shift,np.array([right_edge]), axis = 0)
|
||||||
|
stop_points_right = 0.5 * (start_points + start_points_right_shift)
|
||||||
|
stop_points_right += level_height * DOWN
|
||||||
|
|
||||||
|
|
||||||
|
for (i,(p,q)) in enumerate(zip(start_points,stop_points_left)):
|
||||||
|
|
||||||
|
color = LIGHT_GRAY
|
||||||
|
|
||||||
|
if n % 2 == 0 and i <= n/2:
|
||||||
|
m = n/2 + 0.25
|
||||||
|
jj = i
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 0 and i > n/2:
|
||||||
|
m = n/2 + 0.25
|
||||||
|
jj = n - i + 0.5
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 1 and i <= n/2:
|
||||||
|
m = n/2 + 0.75
|
||||||
|
jj = i
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 1 and i > n/2:
|
||||||
|
m = n/2 + 0.75
|
||||||
|
jj = n - i + 0.5
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
line = Line(p,q, stroke_color = color)
|
||||||
|
self.add(line)
|
||||||
|
|
||||||
|
for (i,(p,q)) in enumerate(zip(start_points,stop_points_right)):
|
||||||
|
|
||||||
|
color = LIGHT_GRAY
|
||||||
|
|
||||||
|
if n % 2 == 0 and i < n/2:
|
||||||
|
m = n/2 + 0.25
|
||||||
|
jj = i + 0.5
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 0 and i >= n/2:
|
||||||
|
m = n/2 + 0.25
|
||||||
|
jj = n - i
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 1 and i <= n/2:
|
||||||
|
m = n/2 + 0.75
|
||||||
|
jj = i + 0.5
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
elif n % 2 == 1 and i > n/2:
|
||||||
|
m = n/2 + 0.75
|
||||||
|
jj = n - i
|
||||||
|
alpha = 1 - float(jj)/m
|
||||||
|
color = rainbow_color(alpha)
|
||||||
|
|
||||||
|
|
||||||
|
line = Line(p,q, stroke_color = color)
|
||||||
|
self.add(line)
|
||||||
|
|
||||||
|
if (n + 1) % dev_y_step == 0 and n != 1:
|
||||||
|
j += dev_x_step
|
||||||
|
dev_stop = stop_points_left[j]
|
||||||
|
line = Line(dev_start,dev_stop,stroke_color = WHITE)
|
||||||
|
self.add(line)
|
||||||
|
dot = Dot(dev_stop, fill_color = WHITE)
|
||||||
|
self.add_foreground_mobject(dot)
|
||||||
|
dev_start = dev_stop
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
start_points = np.append(stop_points_left,[stop_points_right[-1]], axis = 0)
|
||||||
|
|
||||||
|
left_edge += level_height * DOWN
|
||||||
|
right_edge += level_height * DOWN
|
||||||
|
|
||||||
|
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
265
primes.py
Normal file
265
primes.py
Normal file
|
@ -0,0 +1,265 @@
|
||||||
|
from helpers import *
|
||||||
|
from mobject import Mobject
|
||||||
|
from mobject.vectorized_mobject import *
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import *
|
||||||
|
from animation.simple_animations import *
|
||||||
|
from topics.geometry import *
|
||||||
|
from scene import Scene
|
||||||
|
from camera import *
|
||||||
|
from topics.number_line import *
|
||||||
|
from topics.three_dimensions import *
|
||||||
|
from topics.light import *
|
||||||
|
from topics.characters import *
|
||||||
|
from topics.numerals import *
|
||||||
|
|
||||||
|
def is_prime(n):
|
||||||
|
|
||||||
|
for i in primes(n**0.5):
|
||||||
|
if n % i == 0:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def primes(max_n):
|
||||||
|
|
||||||
|
if max_n < 2:
|
||||||
|
return []
|
||||||
|
|
||||||
|
numbers = range(2, int(max_n) + 1)
|
||||||
|
p = []
|
||||||
|
|
||||||
|
while len(numbers) > 0:
|
||||||
|
q = numbers[0]
|
||||||
|
p.append(q)
|
||||||
|
numbers = [x for x in numbers if x % q != 0]
|
||||||
|
|
||||||
|
return p
|
||||||
|
|
||||||
|
def prime_factors(n):
|
||||||
|
|
||||||
|
if is_prime(n):
|
||||||
|
return [n]
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
primes_list = primes(n/2)
|
||||||
|
|
||||||
|
factors = []
|
||||||
|
r = n
|
||||||
|
|
||||||
|
while r >= 2:
|
||||||
|
p = primes_list[i]
|
||||||
|
if r % p == 0:
|
||||||
|
factors.append(p)
|
||||||
|
r = r/p
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return factors
|
||||||
|
|
||||||
|
|
||||||
|
RUN_TIME = 0.5
|
||||||
|
DOWN_SHIFT = 0.0 * DOWN
|
||||||
|
|
||||||
|
class Primes(Scene):
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
|
||||||
|
N = 100
|
||||||
|
|
||||||
|
primes_list = np.array(primes(N))
|
||||||
|
|
||||||
|
palette = ["#FBA125", "#76CD42", "#30CCF5", "#9377C4", "#F95137",
|
||||||
|
# 2 3 5 7 11
|
||||||
|
"#1B442E", TEAL_E, MAROON_A, DARK_BROWN, PINK,
|
||||||
|
# 13 17 19 23 29
|
||||||
|
"#9C25FB", GREEN_E, MAROON_E, GOLD_E, GREEN_E,
|
||||||
|
# 31 37 41 43 47 # last prime to occur in a factorization
|
||||||
|
LIGHT_BROWN, DARK_BLUE, GREY_BROWN, GREEN_C, BLUE_C,
|
||||||
|
# 53 59 61 67 71
|
||||||
|
PURPLE_C, RED_C, YELLOW_E, TEAL_C, MAROON_C]
|
||||||
|
# 73 79 83 89 97
|
||||||
|
|
||||||
|
nb_primes = len(primes_list)
|
||||||
|
print nb_primes
|
||||||
|
|
||||||
|
prime_points_radius = 3.2
|
||||||
|
angles = np.arange(TAU/4, -3*TAU/4, -TAU/float(nb_primes))
|
||||||
|
print len(angles), angles
|
||||||
|
prime_points = [prime_points_radius * (np.cos(theta) * RIGHT
|
||||||
|
+ np.sin(theta) * UP)
|
||||||
|
for theta in angles]
|
||||||
|
print len(prime_points)
|
||||||
|
|
||||||
|
wheel = Wheel()
|
||||||
|
|
||||||
|
angles = [TAU]
|
||||||
|
colors = [LIGHT_GREY]
|
||||||
|
|
||||||
|
wheel.update_sectors(angles, colors)
|
||||||
|
wheel.rotate(-TAU/4).shift(DOWN_SHIFT)
|
||||||
|
self.add(wheel)
|
||||||
|
|
||||||
|
number = DecimalNumber(1, num_decimal_points = 0).scale(2).shift(DOWN_SHIFT)
|
||||||
|
self.add(number)
|
||||||
|
self.wait(RUN_TIME)
|
||||||
|
|
||||||
|
j = 0
|
||||||
|
|
||||||
|
for i in range(2,N+1):
|
||||||
|
|
||||||
|
factors = prime_factors(i)
|
||||||
|
factor_indices = [np.where(primes_list == x)[0][0] for x in factors]
|
||||||
|
|
||||||
|
nb_sectors = float(len(factor_indices))
|
||||||
|
new_angles = np.ones(nb_sectors) / nb_sectors * TAU
|
||||||
|
|
||||||
|
new_colors = []
|
||||||
|
for index in factor_indices:
|
||||||
|
new_colors.append(palette[index])
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
UpdateAngles(wheel, new_angles = new_angles, new_colors = new_colors,
|
||||||
|
run_time = RUN_TIME),
|
||||||
|
ChangeDecimalToValue(number, i, run_time = RUN_TIME)
|
||||||
|
)
|
||||||
|
self.wait(RUN_TIME)
|
||||||
|
|
||||||
|
if is_prime(i):
|
||||||
|
full_wheel = VGroup(wheel,number).copy()
|
||||||
|
full_wheel_copy = full_wheel.copy()
|
||||||
|
full_wheel_copy.scale(0.15).move_to(prime_points[j])
|
||||||
|
print j
|
||||||
|
j += 1
|
||||||
|
self.play(
|
||||||
|
Transform(full_wheel, full_wheel_copy)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Wheel(VMobject):
|
||||||
|
|
||||||
|
CONFIG = {
|
||||||
|
"inner_radius" : 1.2,
|
||||||
|
"outer_radius" : 2.4,
|
||||||
|
"nb_sectors" : 25,
|
||||||
|
"colors" : [BLACK] * 25
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_points(self):
|
||||||
|
|
||||||
|
angle = TAU/self.nb_sectors
|
||||||
|
angle_range = np.arange(0,TAU,angle)
|
||||||
|
for j in range(self.nb_sectors - len(angle_range)):
|
||||||
|
angle_range = np.append(angle_range, TAU)
|
||||||
|
self.colors.append(BLACK)
|
||||||
|
|
||||||
|
for (i,theta) in enumerate(angle_range):
|
||||||
|
if theta != TAU:
|
||||||
|
use_angle = angle
|
||||||
|
else:
|
||||||
|
use_angle = 0
|
||||||
|
sector = AnnularSector(
|
||||||
|
inner_radius = self.inner_radius,
|
||||||
|
outer_radius = self.outer_radius,
|
||||||
|
angle = use_angle,
|
||||||
|
start_angle = theta,
|
||||||
|
fill_color = self.colors[i],
|
||||||
|
fill_opacity = 1,
|
||||||
|
stroke_color = WHITE,
|
||||||
|
stroke_width = 5
|
||||||
|
).rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT)
|
||||||
|
self.add(sector)
|
||||||
|
|
||||||
|
def update_sectors(self, new_angles, new_colors):
|
||||||
|
|
||||||
|
if len(new_angles) > self.nb_sectors:
|
||||||
|
raise "More angles than sectors!"
|
||||||
|
|
||||||
|
for i in range(len(new_angles), self.nb_sectors):
|
||||||
|
new_angles = np.append(new_angles, 0)
|
||||||
|
new_colors.append(BLACK)
|
||||||
|
|
||||||
|
self.colors = new_colors
|
||||||
|
|
||||||
|
new_start_angles = -np.cumsum(new_angles) + new_angles
|
||||||
|
|
||||||
|
for (i,sector) in enumerate(self.submobjects):
|
||||||
|
sector.angle = new_angles[i]
|
||||||
|
sector.start_angle = new_start_angles[i]
|
||||||
|
sector.set_fill(color = new_colors[i])
|
||||||
|
sector.generate_points()
|
||||||
|
sector.rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateAngles(Animation):
|
||||||
|
|
||||||
|
def __init__(self,mobject,**kwargs):
|
||||||
|
|
||||||
|
self.old_angles = []
|
||||||
|
for (i, sector) in enumerate(mobject.submobjects):
|
||||||
|
self.old_angles.append(sector.angle)
|
||||||
|
|
||||||
|
self.old_angles = np.array(self.old_angles)
|
||||||
|
self.old_start_angles = np.cumsum(self.old_angles) - self.old_angles + TAU/4
|
||||||
|
|
||||||
|
digest_config(self, kwargs)
|
||||||
|
Animation.__init__(self,mobject,**kwargs)
|
||||||
|
|
||||||
|
def update_submobject(self, submobject, starting_submobject, alpha):
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
for submob in self.mobject.submobjects:
|
||||||
|
if submobject == submob:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
for j in range(len(self.new_angles), self.mobject.nb_sectors):
|
||||||
|
|
||||||
|
self.new_angles = np.append(self.new_angles, 0)
|
||||||
|
self.new_colors.append(BLACK)
|
||||||
|
|
||||||
|
self.new_start_angles = np.cumsum(self.new_angles) - self.new_angles + TAU/4
|
||||||
|
# this should be in __init__!
|
||||||
|
# but has no effect there
|
||||||
|
|
||||||
|
submobject.angle = interpolate(
|
||||||
|
self.old_angles[i], self.new_angles[i], alpha
|
||||||
|
)
|
||||||
|
submobject.start_angle = interpolate(
|
||||||
|
self.old_start_angles[i],
|
||||||
|
self.new_start_angles[i], alpha
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
interpolated_color = interpolate_color(
|
||||||
|
self.mobject.colors[i],
|
||||||
|
self.new_colors[i],
|
||||||
|
alpha
|
||||||
|
)
|
||||||
|
|
||||||
|
submobject.set_fill(color = interpolated_color)
|
||||||
|
submobject.generate_points()
|
||||||
|
submobject.rotate_about_origin(TAU/2, axis = UP).shift(DOWN_SHIFT)
|
||||||
|
|
||||||
|
if alpha > 0.95:
|
||||||
|
self.mobject.colors[i] = self.new_colors[i]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue