mirror of
https://github.com/3b1b/manim.git
synced 2025-07-31 22:13:30 +08:00
working on basel
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@ -5,4 +5,7 @@ ka_playgrounds/
|
||||
grant_playground.py
|
||||
special_animations.py
|
||||
prettiness_hall_of_fame.py
|
||||
files/
|
||||
files/
|
||||
ben_playground.py
|
||||
|
||||
ben_cairo_test.py
|
||||
|
329
active_projects/basel.py
Normal file
329
active_projects/basel.py
Normal file
@ -0,0 +1,329 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from helpers import *
|
||||
|
||||
from mobject.tex_mobject import TexMobject
|
||||
from mobject import Mobject
|
||||
from mobject.image_mobject import ImageMobject
|
||||
from mobject.vectorized_mobject import *
|
||||
|
||||
from animation.animation import Animation
|
||||
from animation.transform import *
|
||||
from animation.simple_animations import *
|
||||
from animation.playground import *
|
||||
from topics.geometry import *
|
||||
from topics.characters import *
|
||||
from topics.functions import *
|
||||
from topics.number_line import *
|
||||
from topics.numerals import *
|
||||
from topics.combinatorics import *
|
||||
from scene import Scene
|
||||
from camera import Camera
|
||||
from mobject.svg_mobject import *
|
||||
from mobject.tex_mobject import *
|
||||
|
||||
|
||||
from mobject.vectorized_mobject import *
|
||||
|
||||
## To watch one of these scenes, run the following:
|
||||
## python extract_scene.py -p file_name <SceneName>
|
||||
|
||||
|
||||
|
||||
class LightCone(Circle):
|
||||
pass
|
||||
|
||||
|
||||
class IntroScene(PiCreatureScene):
|
||||
|
||||
CONFIG = {
|
||||
"rect_height" : 0.2,
|
||||
"duration" : 1.0,
|
||||
"eq_spacing" : 3 * MED_LARGE_BUFF
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
||||
morty = self.get_primary_pi_creature()
|
||||
morty.scale(0.7).to_corner(DOWN+RIGHT)
|
||||
|
||||
self.build_up_euler_sum()
|
||||
self.force_skipping()
|
||||
self.build_up_sum_on_number_line()
|
||||
self.show_pi_answer()
|
||||
self.other_pi_formulas()
|
||||
self.refocus_on_euler_sum()
|
||||
|
||||
self.revert_to_original_skipping_status()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def build_up_euler_sum(self):
|
||||
|
||||
self.euler_sum = TexMobject(
|
||||
"1", "+",
|
||||
"{1 \\over 4}", "+",
|
||||
"{1 \\over 9}", "+",
|
||||
"{1 \\over 16}", "+",
|
||||
"{1 \\over 25}", "+",
|
||||
"\\cdots", "=",
|
||||
arg_separator = " \\, "
|
||||
)
|
||||
|
||||
self.euler_sum.to_edge(UP)
|
||||
self.euler_sum.shift(2*LEFT)
|
||||
|
||||
terms = [1./n**2 for n in range(1,6)]
|
||||
partial_results_values = np.cumsum(terms)
|
||||
|
||||
self.play(
|
||||
FadeIn(self.euler_sum[0], run_time = self.duration)
|
||||
)
|
||||
|
||||
equals_sign = self.euler_sum.get_part_by_tex("=")
|
||||
|
||||
self.partial_sum_decimal = DecimalNumber(partial_results_values[1],
|
||||
num_decimal_points = 2)
|
||||
self.partial_sum_decimal.next_to(equals_sign, RIGHT)
|
||||
|
||||
|
||||
|
||||
for i in range(4):
|
||||
|
||||
FadeIn(self.partial_sum_decimal, run_time = self.duration)
|
||||
|
||||
if i == 0:
|
||||
|
||||
|
||||
self.play(
|
||||
FadeIn(self.euler_sum[1], run_time = self.duration),
|
||||
FadeIn(self.euler_sum[2], run_time = self.duration),
|
||||
FadeIn(equals_sign, run_time = self.duration),
|
||||
FadeIn(self.partial_sum_decimal, run_time = self.duration)
|
||||
)
|
||||
|
||||
else:
|
||||
self.play(
|
||||
FadeIn(self.euler_sum[2*i+1], run_time = self.duration),
|
||||
FadeIn(self.euler_sum[2*i+2], run_time = self.duration),
|
||||
ChangeDecimalToValue(
|
||||
self.partial_sum_decimal,
|
||||
partial_results_values[i+1],
|
||||
run_time = self.duration,
|
||||
num_decimal_points = 6,
|
||||
show_ellipsis = True,
|
||||
position_update_func = lambda m: m.next_to(equals_sign, RIGHT)
|
||||
)
|
||||
)
|
||||
|
||||
self.wait()
|
||||
|
||||
self.q_marks = TextMobject("???").highlight(YELLOW)
|
||||
self.q_marks.move_to(self.partial_sum_decimal)
|
||||
|
||||
self.play(
|
||||
FadeIn(self.euler_sum[-3], run_time = self.duration), # +
|
||||
FadeIn(self.euler_sum[-2], run_time = self.duration), # ...
|
||||
ReplacementTransform(self.partial_sum_decimal, self.q_marks)
|
||||
)
|
||||
|
||||
|
||||
|
||||
def build_up_sum_on_number_line(self):
|
||||
|
||||
self.number_line = NumberLine(
|
||||
x_min = 0,
|
||||
color = WHITE,
|
||||
number_at_center = 1,
|
||||
stroke_width = 1,
|
||||
numbers_with_elongated_ticks = [0,1,2,3],
|
||||
numbers_to_show = np.arange(0,5),
|
||||
unit_size = 5,
|
||||
tick_frequency = 0.2,
|
||||
line_to_number_buff = MED_LARGE_BUFF
|
||||
)
|
||||
|
||||
self.number_line_labels = self.number_line.get_number_mobjects()
|
||||
self.add(self.number_line,self.number_line_labels)
|
||||
self.wait()
|
||||
|
||||
# create slabs for series terms
|
||||
|
||||
max_n = 10
|
||||
|
||||
terms = [0] + [1./(n**2) for n in range(1, max_n + 1)]
|
||||
series_terms = np.cumsum(terms)
|
||||
lines = VGroup()
|
||||
self.rects = VGroup()
|
||||
slab_colors = [YELLOW, BLUE] * (max_n / 2)
|
||||
|
||||
for t1, t2, color in zip(series_terms, series_terms[1:], slab_colors):
|
||||
line = Line(*map(self.number_line.number_to_point, [t1, t2]))
|
||||
rect = Rectangle()
|
||||
rect.stroke_width = 0
|
||||
rect.fill_opacity = 1
|
||||
rect.highlight(color)
|
||||
rect.stretch_to_fit_height(
|
||||
self.rect_height,
|
||||
)
|
||||
rect.stretch_to_fit_width(line.get_width())
|
||||
rect.move_to(line)
|
||||
|
||||
self.rects.add(rect)
|
||||
lines.add(line)
|
||||
|
||||
#self.rects.radial_gradient_highlight(ORIGIN, 5, RED, BLUE)
|
||||
|
||||
for i in range(5):
|
||||
self.play(
|
||||
GrowFromPoint(self.rects[i], self.euler_sum[2*i].get_center(),
|
||||
run_time = self.duration)
|
||||
)
|
||||
|
||||
for i in range(5, max_n):
|
||||
self.play(
|
||||
GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(),
|
||||
run_time = self.duration)
|
||||
)
|
||||
|
||||
|
||||
def show_pi_answer(self):
|
||||
|
||||
self.pi_answer = TexMobject("{\\pi^2 \\over 6}").highlight(YELLOW)
|
||||
self.pi_answer.move_to(self.partial_sum_decimal)
|
||||
self.pi_answer.next_to(self.euler_sum[-1], RIGHT,
|
||||
submobject_to_align = self.pi_answer[-2])
|
||||
self.play(ReplacementTransform(self.q_marks, self.pi_answer))
|
||||
|
||||
|
||||
def other_pi_formulas(self):
|
||||
|
||||
self.play(
|
||||
FadeOut(self.rects),
|
||||
FadeOut(self.number_line_labels),
|
||||
FadeOut(self.number_line)
|
||||
)
|
||||
|
||||
self.leibniz_sum = TexMobject(
|
||||
"1-{1\\over 3}+{1\\over 5}-{1\\over 7}+{1\\over 9}-\\cdots",
|
||||
"=", "{\\pi \\over 4}")
|
||||
|
||||
self.wallis_product = TexMobject(
|
||||
"{2\\over 1} \\cdot {2\\over 3} \\cdot {4\\over 3} \\cdot {4\\over 5}" +
|
||||
"\\cdot {6\\over 5} \\cdot {6\\over 7} \\cdots",
|
||||
"=", "{\\pi \\over 2}")
|
||||
|
||||
self.leibniz_sum.next_to(self.euler_sum.get_part_by_tex("="), DOWN,
|
||||
buff = self.eq_spacing,
|
||||
submobject_to_align = self.leibniz_sum.get_part_by_tex("=")
|
||||
)
|
||||
|
||||
self.wallis_product.next_to(self.leibniz_sum.get_part_by_tex("="), DOWN,
|
||||
buff = self.eq_spacing,
|
||||
submobject_to_align = self.wallis_product.get_part_by_tex("=")
|
||||
)
|
||||
|
||||
|
||||
self.play(
|
||||
Write(self.leibniz_sum)
|
||||
)
|
||||
self.play(
|
||||
Write(self.wallis_product)
|
||||
)
|
||||
|
||||
|
||||
|
||||
def refocus_on_euler_sum(self):
|
||||
|
||||
self.euler_sum.add(self.pi_answer)
|
||||
|
||||
self.play(
|
||||
FadeOut(self.leibniz_sum),
|
||||
FadeOut(self.wallis_product),
|
||||
ApplyMethod(self.euler_sum.shift,
|
||||
ORIGIN + 2*UP - self.euler_sum.get_center())
|
||||
)
|
||||
|
||||
# focus on pi squared
|
||||
pi_squared = self.euler_sum.get_part_by_tex("\\pi")[-3]
|
||||
self.play(
|
||||
ScaleInPlace(pi_squared,2,rate_func = wiggle)
|
||||
)
|
||||
|
||||
morty = self.get_primary_pi_creature()
|
||||
bubble = ThoughtBubble(height = 2, width = 5)
|
||||
bubble.pin_to(morty)
|
||||
|
||||
thought = Circle()
|
||||
thought.add(TexMobject("?"))
|
||||
thought.next_to(morty,LEFT, LARGE_BUFF, UP)
|
||||
thought.generate_target()
|
||||
|
||||
bubble.add_content(thought.target)
|
||||
|
||||
self.play(
|
||||
morty.change_mode, "confused",
|
||||
FadeIn(bubble)
|
||||
)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
class FirstLightHouseScene(Scene):
|
||||
|
||||
def construct(self):
|
||||
self.show_lighthouses_on_number_line()
|
||||
|
||||
|
||||
|
||||
def show_lighthouses_on_number_line(self):
|
||||
|
||||
self.number_line = NumberLine(
|
||||
x_min = 0,
|
||||
color = WHITE,
|
||||
number_at_center = 1,
|
||||
stroke_width = 1,
|
||||
numbers_with_elongated_ticks = [0,1,2,3],
|
||||
numbers_to_show = np.arange(0,5),
|
||||
unit_size = 5,
|
||||
tick_frequency = 0.2,
|
||||
line_to_number_buff = MED_LARGE_BUFF
|
||||
)
|
||||
|
||||
self.number_line_labels = self.number_line.get_number_mobjects()
|
||||
self.add(self.number_line,self.number_line_labels)
|
||||
self.wait()
|
||||
|
||||
first_lighthouse_indicator = Circle(
|
||||
stroke_width = 1,
|
||||
stroke_color = WHITE,
|
||||
fill_color = RED,
|
||||
fill_opacity = 1,
|
||||
radius = 0.3
|
||||
)
|
||||
|
||||
origin_point = self.number_line.number_to_point(0)
|
||||
first_lighthouse_indicator.move_to(origin_point)
|
||||
|
||||
self.add(first_lighthouse_indicator)
|
||||
|
||||
lighthouse1 = SVGMobject(file_name = "lighthouse")
|
||||
self.add(lighthouse1)
|
||||
|
||||
self.wait()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -436,6 +436,10 @@ class Mobject(object):
|
||||
self.submobject_gradient_highlight(*colors)
|
||||
return self
|
||||
|
||||
def radial_gradient_highlight(self, center = None, radius = 1, inner_color = WHITE, outer_color = BLACK):
|
||||
self.submobject_radial_gradient_highlight(center, radius, inner_color, outer_color)
|
||||
return self
|
||||
|
||||
def submobject_gradient_highlight(self, *colors):
|
||||
if len(colors) == 0:
|
||||
raise Exception("Need at least one color")
|
||||
@ -444,10 +448,28 @@ class Mobject(object):
|
||||
|
||||
mobs = self.family_members_with_points()
|
||||
new_colors = color_gradient(colors, len(mobs))
|
||||
|
||||
for mob, color in zip(mobs, new_colors):
|
||||
mob.highlight(color, family = False)
|
||||
return self
|
||||
|
||||
def submobject_radial_gradient_highlight(self, center = None, radius = 1, inner_color = WHITE, outer_color = BLACK):
|
||||
|
||||
mobs = self.family_members_with_points()
|
||||
if center == None:
|
||||
center = self.get_center()
|
||||
|
||||
for mob in self.family_members_with_points():
|
||||
t = np.linalg.norm(mob.get_center() - center)/radius
|
||||
t = min(t,1)
|
||||
print t
|
||||
mob_color = interpolate_color(inner_color, outer_color, t)
|
||||
print mob_color
|
||||
mob.highlight(mob_color, family = False)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def set_color(self, color):
|
||||
self.highlight(color)
|
||||
self.color = Color(color)
|
||||
|
@ -54,6 +54,19 @@ class PMobject(Mobject):
|
||||
])
|
||||
return self
|
||||
|
||||
def radial_gradient_highlight(self, center = None, radius = 1, inner_color = WHITE, outer_color = BLACK):
|
||||
start_rgba, end_rgba = map(color_to_rgba, [start_color, end_color])
|
||||
if center == None:
|
||||
center = self.get_center()
|
||||
for mob in self.family_members_with_points():
|
||||
num_points = mob.get_num_points()
|
||||
t = min(1,np.abs(mob.get_center() - center)/radius)
|
||||
|
||||
mob.rgbas = np.array(
|
||||
[ interpolate(start_rgba, end_rgba, t) ] * num_points
|
||||
)
|
||||
return self
|
||||
|
||||
def match_colors(self, mobject):
|
||||
Mobject.align_data(self, mobject)
|
||||
self.rgbas = np.array(mobject.rgbas)
|
||||
|
@ -9,7 +9,8 @@ from helpers import *
|
||||
class DecimalNumber(VMobject):
|
||||
CONFIG = {
|
||||
"num_decimal_points" : 2,
|
||||
"digit_to_digit_buff" : 0.05
|
||||
"digit_to_digit_buff" : 0.05,
|
||||
"show_ellipsis" : False
|
||||
}
|
||||
def __init__(self, number, **kwargs):
|
||||
digest_config(self, kwargs, locals())
|
||||
@ -18,6 +19,10 @@ class DecimalNumber(VMobject):
|
||||
TexMobject(char)
|
||||
for char in num_string
|
||||
], **kwargs)
|
||||
|
||||
if self.show_ellipsis:
|
||||
self.add(TexMobject("\\dots"))
|
||||
|
||||
self.arrange_submobjects(
|
||||
buff = self.digit_to_digit_buff,
|
||||
aligned_edge = DOWN
|
||||
@ -46,6 +51,7 @@ class Integer(VGroup):
|
||||
class ChangingDecimal(Animation):
|
||||
CONFIG = {
|
||||
"num_decimal_points" : None,
|
||||
"show_ellipsis" : None,
|
||||
"spare_parts" : 2,
|
||||
"position_update_func" : None,
|
||||
"tracked_mobject" : None
|
||||
@ -54,6 +60,8 @@ class ChangingDecimal(Animation):
|
||||
digest_config(self, kwargs, locals())
|
||||
if self.num_decimal_points is None:
|
||||
self.num_decimal_points = decimal_number_mobject.num_decimal_points
|
||||
if self.show_ellipsis is None:
|
||||
self.show_ellipsis = decimal_number_mobject.show_ellipsis
|
||||
decimal_number_mobject.add(*[
|
||||
VectorizedPoint(decimal_number_mobject.get_corner(DOWN+LEFT))
|
||||
for x in range(self.spare_parts)]
|
||||
@ -71,7 +79,9 @@ class ChangingDecimal(Animation):
|
||||
decimal = self.decimal_number_mobject
|
||||
new_number = self.number_update_func(alpha)
|
||||
new_decimal = DecimalNumber(
|
||||
new_number, num_decimal_points = self.num_decimal_points
|
||||
new_number,
|
||||
num_decimal_points = self.num_decimal_points,
|
||||
show_ellipsis = self.show_ellipsis
|
||||
)
|
||||
new_decimal.replace(decimal, dim_to_match = 1)
|
||||
new_decimal.highlight(decimal.get_color())
|
||||
|
Reference in New Issue
Block a user