mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
First draft of music and measure theory
This commit is contained in:
parent
0d60cf6207
commit
42a8e166f0
7 changed files with 739 additions and 68 deletions
|
@ -92,38 +92,44 @@ class Grid(Mobject1D):
|
||||||
|
|
||||||
class NumberLine(Mobject1D):
|
class NumberLine(Mobject1D):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"radius" : SPACE_WIDTH,
|
"color" : "skyblue",
|
||||||
|
"numerical_radius" : SPACE_WIDTH,
|
||||||
"unit_length_to_spacial_width" : 1,
|
"unit_length_to_spacial_width" : 1,
|
||||||
"tick_size" : 0.1,
|
"tick_size" : 0.1,
|
||||||
"tick_frequency" : 0.5,
|
"tick_frequency" : 0.5,
|
||||||
|
"leftmost_tick" : -int(SPACE_WIDTH),
|
||||||
"number_at_center" : 0,
|
"number_at_center" : 0,
|
||||||
"numbers_with_elongated_ticks" : [0],
|
"numbers_with_elongated_ticks" : [0],
|
||||||
"longer_tick_multiple" : 2,
|
"longer_tick_multiple" : 2,
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
digest_config(self, NumberLine, kwargs)
|
digest_config(self, NumberLine, kwargs)
|
||||||
numerical_radius = float(self.radius) / self.unit_length_to_spacial_width
|
self.left_num = self.number_at_center - self.numerical_radius
|
||||||
self.left_num = self.number_at_center - numerical_radius
|
self.right_num = self.number_at_center + self.numerical_radius
|
||||||
self.right_num = self.number_at_center + numerical_radius
|
|
||||||
Mobject1D.__init__(self, **kwargs)
|
Mobject1D.__init__(self, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
|
spacial_radius = self.numerical_radius*self.unit_length_to_spacial_width
|
||||||
self.add_points([
|
self.add_points([
|
||||||
(b*x, 0, 0)
|
(b*x, 0, 0)
|
||||||
for x in np.arange(0, self.radius, self.epsilon)
|
for x in np.arange(0, spacial_radius, self.epsilon)
|
||||||
for b in [-1, 1]
|
for b in [-1, 1]
|
||||||
])
|
])
|
||||||
self.index_of_left = np.argmin(self.points[:,0])
|
self.index_of_left = np.argmin(self.points[:,0])
|
||||||
self.index_of_right = np.argmax(self.points[:,0])
|
self.index_of_right = np.argmax(self.points[:,0])
|
||||||
spacial_tick_frequency = self.tick_frequency*self.unit_length_to_spacial_width
|
spacial_tick_frequency = self.tick_frequency*self.unit_length_to_spacial_width
|
||||||
self.add_points([
|
self.add_points([
|
||||||
(b*x, y, 0)
|
(x, y, 0)
|
||||||
for x in np.arange(0, self.radius, spacial_tick_frequency)
|
for num in self.get_tick_numbers()
|
||||||
for y in np.arange(-self.tick_size, self.tick_size, self.epsilon)
|
for y in np.arange(-self.tick_size, self.tick_size, self.epsilon)
|
||||||
for b in ([1, -1] if x > 0 else [1])
|
for x in [self.number_to_point(num)[0]]
|
||||||
])
|
])
|
||||||
for number in self.numbers_with_elongated_ticks:
|
for number in self.numbers_with_elongated_ticks:
|
||||||
self.elongate_tick_at(number, self.longer_tick_multiple)
|
self.elongate_tick_at(number, self.longer_tick_multiple)
|
||||||
|
self.number_of_points_without_numbers = self.get_num_points()
|
||||||
|
|
||||||
|
def get_tick_numbers(self):
|
||||||
|
return np.arange(self.leftmost_tick, self.right_num, self.tick_frequency)
|
||||||
|
|
||||||
def elongate_tick_at(self, number, multiple = 2):
|
def elongate_tick_at(self, number, multiple = 2):
|
||||||
x = self.number_to_point(number)[0]
|
x = self.number_to_point(number)[0]
|
||||||
|
@ -144,21 +150,51 @@ class NumberLine(Mobject1D):
|
||||||
float(number-self.left_num)/(self.right_num - self.left_num)
|
float(number-self.left_num)/(self.right_num - self.left_num)
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_numbers(self, *numbers):
|
def point_to_number(self, point):
|
||||||
|
return self.number_at_center + point[0]/self.unit_length_to_spacial_width
|
||||||
|
|
||||||
|
def default_numbers_to_display(self):
|
||||||
|
return self.get_tick_numbers()[::2]
|
||||||
|
|
||||||
|
def get_vertical_number_offset(self):
|
||||||
|
return 4*DOWN*self.tick_size
|
||||||
|
|
||||||
|
def get_number_mobjects(self, *numbers):
|
||||||
if len(numbers) == 0:
|
if len(numbers) == 0:
|
||||||
numbers = range(int(self.left_num), int(self.right_num+1))
|
numbers = self.default_numbers_to_display()
|
||||||
|
log_spacing = int(np.log10(self.tick_frequency))
|
||||||
|
if log_spacing < 0:
|
||||||
|
num_decimal_places = 2-log_spacing
|
||||||
|
else:
|
||||||
|
num_decimal_places = 1+log_spacing
|
||||||
|
result = []
|
||||||
for number in numbers:
|
for number in numbers:
|
||||||
mob = tex_mobject(str(number)).scale(0.5)
|
if number < 0:
|
||||||
|
num_string = str(number)[:num_decimal_places+1]
|
||||||
|
else:
|
||||||
|
num_string = str(number)[:num_decimal_places]
|
||||||
|
mob = tex_mobject(num_string)
|
||||||
|
vert_scale = 2*self.tick_size/mob.get_height()
|
||||||
|
hori_scale = self.tick_frequency*self.unit_length_to_spacial_width/mob.get_width()
|
||||||
|
mob.scale(min(vert_scale, hori_scale))
|
||||||
mob.shift(self.number_to_point(number))
|
mob.shift(self.number_to_point(number))
|
||||||
mob.shift(DOWN*4*self.tick_size)
|
mob.shift(self.get_vertical_number_offset())
|
||||||
self.add(mob)
|
result.append(mob)
|
||||||
return self
|
return result
|
||||||
|
|
||||||
|
def add_numbers(self, *numbers):
|
||||||
|
self.add(*self.get_number_mobjects(*numbers))
|
||||||
|
|
||||||
|
def remove_numbers(self):
|
||||||
|
self.points = self.points[:self.number_of_points_without_numbers]
|
||||||
|
self.rgbs = self.rgbs[:self.number_of_points_without_numbers]
|
||||||
|
|
||||||
class UnitInterval(NumberLine):
|
class UnitInterval(NumberLine):
|
||||||
DEFAULT_CONFIG = {
|
DEFAULT_CONFIG = {
|
||||||
"radius" : SPACE_WIDTH-1,
|
"numerical_radius" : 0.5,
|
||||||
"unit_length_to_spacial_width" : 2*(SPACE_WIDTH-1),
|
"unit_length_to_spacial_width" : 2*(SPACE_WIDTH-1),
|
||||||
"tick_frequency" : 0.1,
|
"tick_frequency" : 0.1,
|
||||||
|
"leftmost_tick" : 0,
|
||||||
"number_at_center" : 0.5,
|
"number_at_center" : 0.5,
|
||||||
"numbers_with_elongated_ticks" : [0, 1],
|
"numbers_with_elongated_ticks" : [0, 1],
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ class ImageMobject(Mobject):
|
||||||
"invert" : True,
|
"invert" : True,
|
||||||
"use_cache" : True,
|
"use_cache" : True,
|
||||||
"should_buffer_points" : False,
|
"should_buffer_points" : False,
|
||||||
"scale_value" : 1.0
|
"scale_value" : 1.0,
|
||||||
|
"should_center" : True
|
||||||
}
|
}
|
||||||
def __init__(self, image_file, **kwargs):
|
def __init__(self, image_file, **kwargs):
|
||||||
digest_config(self, ImageMobject, kwargs, locals())
|
digest_config(self, ImageMobject, kwargs, locals())
|
||||||
|
@ -35,7 +36,8 @@ class ImageMobject(Mobject):
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
self.generate_points_from_file(path)
|
self.generate_points_from_file(path)
|
||||||
self.scale(self.scale_value)
|
self.scale(self.scale_value)
|
||||||
self.center()
|
if self.should_center:
|
||||||
|
self.center()
|
||||||
return
|
return
|
||||||
raise IOError("File not Found")
|
raise IOError("File not Found")
|
||||||
|
|
||||||
|
@ -135,12 +137,19 @@ def tex_mobject(expression,
|
||||||
size = "\\large"
|
size = "\\large"
|
||||||
#Todo, make this more sophisticated.
|
#Todo, make this more sophisticated.
|
||||||
image_files = tex_to_image(expression, size, template_tex_file)
|
image_files = tex_to_image(expression, size, template_tex_file)
|
||||||
|
config = {
|
||||||
|
"should_buffer_points" : True,
|
||||||
|
"should_center" : False,
|
||||||
|
}
|
||||||
if isinstance(image_files, list):
|
if isinstance(image_files, list):
|
||||||
#TODO, is checking listiness really the best here?
|
#TODO, is checking listiness really the best here?
|
||||||
result = CompoundMobject(*map(ImageMobject, image_files))
|
result = CompoundMobject(*[
|
||||||
|
ImageMobject(f, **config)
|
||||||
|
for f in image_files
|
||||||
|
])
|
||||||
else:
|
else:
|
||||||
result = ImageMobject(image_files, should_buffer_points = True)
|
result = ImageMobject(image_files, **config)
|
||||||
return result.highlight("white")
|
return result.center().highlight("white")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Point(Mobject):
|
||||||
Mobject.__init__(self, **kwargs)
|
Mobject.__init__(self, **kwargs)
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
self.add_points(self.location)
|
self.add_points([self.location])
|
||||||
|
|
||||||
|
|
||||||
class Dot(Mobject1D): #Use 1D density, even though 2D
|
class Dot(Mobject1D): #Use 1D density, even though 2D
|
||||||
|
|
|
@ -10,16 +10,18 @@ from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene
|
from scene import Scene, NumberLineScene
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
|
|
||||||
|
|
||||||
class SampleScene(Scene):
|
class SampleScene(NumberLineScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
square = Square()
|
NumberLineScene.construct(self)
|
||||||
self.add(square)
|
arrow = Arrow(2*RIGHT+UP, 2*RIGHT)
|
||||||
self.dither()
|
self.add(arrow)
|
||||||
self.play(Flash(square))
|
self.dither(2)
|
||||||
|
self.zoom_in_on(2.4, zoom_factor = 10)
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
command_line_create_scene()
|
command_line_create_scene()
|
|
@ -3,4 +3,5 @@ from sub_scenes import *
|
||||||
from arithmetic_scenes import *
|
from arithmetic_scenes import *
|
||||||
from counting_scene import *
|
from counting_scene import *
|
||||||
from pascals_triangle import *
|
from pascals_triangle import *
|
||||||
from scene_from_video import *
|
from scene_from_video import *
|
||||||
|
from number_line import *
|
|
@ -12,4 +12,67 @@ from helpers import *
|
||||||
|
|
||||||
class NumberLineScene(Scene):
|
class NumberLineScene(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
self.number_line = NumberLine()
|
||||||
|
self.displayed_numbers = self.number_line.default_numbers_to_display()
|
||||||
|
self.number_mobs = self.number_line.get_number_mobjects(*self.displayed_numbers)
|
||||||
|
self.add(self.number_line, *self.number_mobs)
|
||||||
|
|
||||||
|
def zoom_in_on(self, number, zoom_factor, run_time = 2.0):
|
||||||
|
unit_length_to_spacial_width = self.number_line.unit_length_to_spacial_width*zoom_factor
|
||||||
|
radius = SPACE_WIDTH/unit_length_to_spacial_width
|
||||||
|
tick_frequency = 10**(np.floor(np.log10(radius)))
|
||||||
|
left_tick = tick_frequency*(np.ceil((number-radius)/tick_frequency))
|
||||||
|
new_number_line = NumberLine(
|
||||||
|
numerical_radius = radius,
|
||||||
|
unit_length_to_spacial_width = unit_length_to_spacial_width,
|
||||||
|
tick_frequency = tick_frequency,
|
||||||
|
leftmost_tick = left_tick,
|
||||||
|
number_at_center = number
|
||||||
|
)
|
||||||
|
new_displayed_numbers = new_number_line.default_numbers_to_display()
|
||||||
|
new_number_mobs = new_number_line.get_number_mobjects(*new_displayed_numbers)
|
||||||
|
|
||||||
|
transforms = []
|
||||||
|
additional_mobjects = []
|
||||||
|
squished_new_line = deepcopy(new_number_line)
|
||||||
|
squished_new_line.scale(1.0/zoom_factor)
|
||||||
|
squished_new_line.shift(self.number_line.number_to_point(number))
|
||||||
|
squished_new_line.points[:,1] = self.number_line.number_to_point(0)[1]
|
||||||
|
transforms.append(Transform(squished_new_line, new_number_line))
|
||||||
|
for mob, num in zip(new_number_mobs, new_displayed_numbers):
|
||||||
|
point = Point(self.number_line.number_to_point(num))
|
||||||
|
point.shift(new_number_line.get_vertical_number_offset())
|
||||||
|
transforms.append(Transform(point, mob))
|
||||||
|
for mob in self.mobjects:
|
||||||
|
if mob == self.number_line:
|
||||||
|
new_mob = deepcopy(mob)
|
||||||
|
new_mob.shift(-self.number_line.number_to_point(number))
|
||||||
|
new_mob.stretch(zoom_factor, 0)
|
||||||
|
transforms.append(Transform(mob, new_mob))
|
||||||
|
continue
|
||||||
|
mob_center = mob.get_center()
|
||||||
|
number_under_center = self.number_line.point_to_number(mob_center)
|
||||||
|
new_point = new_number_line.number_to_point(number_under_center)
|
||||||
|
new_point += mob_center[1]*UP
|
||||||
|
if mob in self.number_mobs:
|
||||||
|
transforms.append(Transform(mob, Point(new_point)))
|
||||||
|
else:
|
||||||
|
transforms.append(ApplyMethod(mob.shift, new_point - mob_center))
|
||||||
|
additional_mobjects.append(mob)
|
||||||
|
line_to_hide_pixelation = Line(
|
||||||
|
self.number_line.get_left(),
|
||||||
|
self.number_line.get_right(),
|
||||||
|
color = self.number_line.get_color()
|
||||||
|
)
|
||||||
|
self.add(line_to_hide_pixelation)
|
||||||
|
self.play(*transforms, run_time = run_time)
|
||||||
|
self.clear()
|
||||||
|
self.number_line = new_number_line
|
||||||
|
self.displayed_numbers = new_displayed_numbers
|
||||||
|
self.number_mobs = new_number_mobs
|
||||||
|
self.add(self.number_line, *self.number_mobs)
|
||||||
|
self.add(*additional_mobjects)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from animation import *
|
||||||
from mobject import *
|
from mobject import *
|
||||||
from constants import *
|
from constants import *
|
||||||
from region import *
|
from region import *
|
||||||
from scene import Scene
|
from scene import Scene, NumberLineScene
|
||||||
from script_wrapper import command_line_create_scene
|
from script_wrapper import command_line_create_scene
|
||||||
from inventing_math import underbrace
|
from inventing_math import underbrace
|
||||||
|
|
||||||
|
@ -64,15 +64,32 @@ def zero_to_one_interval():
|
||||||
interval.add(tex_mobject("1").shift(INTERVAL_RADIUS*RIGHT+DOWN))
|
interval.add(tex_mobject("1").shift(INTERVAL_RADIUS*RIGHT+DOWN))
|
||||||
return interval
|
return interval
|
||||||
|
|
||||||
class OpenInterval(Mobject):
|
class LeftParen(Mobject):
|
||||||
|
def generate_points(self):
|
||||||
|
self.add(tex_mobject("("))
|
||||||
|
self.center()
|
||||||
|
|
||||||
|
def get_center(self):
|
||||||
|
return Mobject.get_center(self) + 0.04*LEFT
|
||||||
|
|
||||||
|
class RightParen(Mobject):
|
||||||
|
def generate_points(self):
|
||||||
|
self.add(tex_mobject(")"))
|
||||||
|
self.center()
|
||||||
|
|
||||||
|
def get_center(self):
|
||||||
|
return Mobject.get_center(self) + 0.04*RIGHT
|
||||||
|
|
||||||
|
|
||||||
|
class OpenInterval(CompoundMobject):
|
||||||
def __init__(self, center_point = ORIGIN, width = 2, **kwargs):
|
def __init__(self, center_point = ORIGIN, width = 2, **kwargs):
|
||||||
digest_config(self, OpenInterval, kwargs, locals())
|
digest_config(self, OpenInterval, kwargs, locals())
|
||||||
Mobject.__init__(self, **kwargs)
|
left = LeftParen().shift(LEFT*width/2)
|
||||||
self.add(tex_mobject("(").shift(LEFT))
|
right = RightParen().shift(RIGHT*width/2)
|
||||||
self.add(tex_mobject(")").shift(RIGHT))
|
CompoundMobject.__init__(self, left, right, **kwargs)
|
||||||
scale_factor = width / 2.0
|
# scale_factor = width / 2.0
|
||||||
self.stretch(scale_factor, 0)
|
# self.stretch(scale_factor, 0)
|
||||||
self.stretch(0.5+0.5*scale_factor, 1)
|
# self.stretch(0.5+0.5*scale_factor, 1)
|
||||||
self.shift(center_point)
|
self.shift(center_point)
|
||||||
|
|
||||||
class Piano(ImageMobject):
|
class Piano(ImageMobject):
|
||||||
|
@ -138,10 +155,12 @@ class VibratingString(Animation):
|
||||||
self.mobject.shift(self.center)
|
self.mobject.shift(self.center)
|
||||||
|
|
||||||
|
|
||||||
class IntervalScene(Scene):
|
class IntervalScene(NumberLineScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.interval = UnitInterval()
|
self.number_line = UnitInterval()
|
||||||
self.add(self.interval)
|
self.displayed_numbers = [0, 1]
|
||||||
|
self.number_mobs = self.number_line.get_number_mobjects(*self.displayed_numbers)
|
||||||
|
self.add(self.number_line, *self.number_mobs)
|
||||||
|
|
||||||
def show_all_fractions(self,
|
def show_all_fractions(self,
|
||||||
num_fractions = 27,
|
num_fractions = 27,
|
||||||
|
@ -155,8 +174,8 @@ class IntervalScene(Scene):
|
||||||
self.remove(frac_mob, tick)
|
self.remove(frac_mob, tick)
|
||||||
|
|
||||||
def add_fraction(self, fraction, shrink = False):
|
def add_fraction(self, fraction, shrink = False):
|
||||||
point = self.num_to_point(fraction)
|
point = self.number_line.number_to_point(fraction)
|
||||||
tick_rad = self.interval.tick_size*TICK_STRETCH_FACTOR
|
tick_rad = self.number_line.tick_size*TICK_STRETCH_FACTOR
|
||||||
frac_mob = fraction_mobject(fraction)
|
frac_mob = fraction_mobject(fraction)
|
||||||
if shrink:
|
if shrink:
|
||||||
scale_factor = 2.0/fraction.denominator
|
scale_factor = 2.0/fraction.denominator
|
||||||
|
@ -168,27 +187,60 @@ class IntervalScene(Scene):
|
||||||
self.add(frac_mob, tick)
|
self.add(frac_mob, tick)
|
||||||
return frac_mob, tick
|
return frac_mob, tick
|
||||||
|
|
||||||
|
def add_fraction_ticks(self, num_fractions = 1000, run_time = 0):
|
||||||
|
long_tick_size = self.number_line.tick_size*TICK_STRETCH_FACTOR
|
||||||
|
all_ticks = []
|
||||||
|
for frac, count in zip(rationals(), range(num_fractions)):
|
||||||
|
point = self.number_line.number_to_point(frac)
|
||||||
|
tick_rad = 2.0*long_tick_size/frac.denominator
|
||||||
|
tick = Line(point+tick_rad*DOWN, point+tick_rad*UP)
|
||||||
|
tick.highlight("yellow")
|
||||||
|
all_ticks.append(tick)
|
||||||
|
all_ticks = CompoundMobject(*all_ticks)
|
||||||
|
if run_time > 0:
|
||||||
|
self.play(ShowCreation(all_ticks))
|
||||||
|
else:
|
||||||
|
self.add(all_ticks)
|
||||||
|
return all_ticks
|
||||||
|
|
||||||
|
|
||||||
def cover_fractions(self,
|
def cover_fractions(self,
|
||||||
epsilon = 0.3,
|
epsilon = 0.3,
|
||||||
num_fractions = 9,
|
num_fractions = 10,
|
||||||
run_time_per_interval = 0.5):
|
run_time_per_interval = 0.5):
|
||||||
for fraction, count in zip(rationals(), range(num_fractions)):
|
intervals = []
|
||||||
self.add_open_interval(
|
lines = []
|
||||||
|
num_intervals = 0
|
||||||
|
all_rationals = rationals()
|
||||||
|
count = 0
|
||||||
|
while True:
|
||||||
|
fraction = all_rationals.next()
|
||||||
|
count += 1
|
||||||
|
if num_intervals >= num_fractions:
|
||||||
|
break
|
||||||
|
if fraction < self.number_line.left_num or fraction > self.number_line.right_num:
|
||||||
|
continue
|
||||||
|
num_intervals += 1
|
||||||
|
interval, line = self.add_open_interval(
|
||||||
fraction,
|
fraction,
|
||||||
epsilon / 2**(count+1),
|
epsilon / min(2**count, 2**30),
|
||||||
run_time = run_time_per_interval
|
run_time = run_time_per_interval
|
||||||
)
|
)
|
||||||
|
intervals.append(interval)
|
||||||
|
lines.append(line)
|
||||||
|
return intervals, lines
|
||||||
|
|
||||||
def add_open_interval(self, num, width, color = None, run_time = 0):
|
def add_open_interval(self, num, width, color = None, run_time = 0):
|
||||||
width *= self.interval.unit_length_to_spacial_width
|
spacial_width = width*self.number_line.unit_length_to_spacial_width
|
||||||
center_point = self.interval.number_to_point(num)
|
center_point = self.number_line.number_to_point(num)
|
||||||
open_interval = OpenInterval(center_point, width)
|
open_interval = OpenInterval(center_point, spacial_width)
|
||||||
if color:
|
color = color or "yellow"
|
||||||
open_interval.highlight(color)
|
interval_line = Line(
|
||||||
interval_line = Line(open_interval.get_left(), open_interval.get_right())
|
center_point+spacial_width*LEFT/2,
|
||||||
interval_line.scale_in_place(0.9)#Silliness
|
center_point+spacial_width*RIGHT/2
|
||||||
|
)
|
||||||
interval_line.do_in_place(interval_line.sort_points, np.linalg.norm)
|
interval_line.do_in_place(interval_line.sort_points, np.linalg.norm)
|
||||||
interval_line.highlight("yellow")
|
interval_line.highlight(color)
|
||||||
if run_time > 0:
|
if run_time > 0:
|
||||||
squished_interval = deepcopy(open_interval).stretch_to_fit_width(0)
|
squished_interval = deepcopy(open_interval).stretch_to_fit_width(0)
|
||||||
self.play(
|
self.play(
|
||||||
|
@ -647,55 +699,123 @@ class PowersOfTwelfthRoot(Scene):
|
||||||
self.play(ShimmerIn(CompoundMobject(*mob_list), run_time = 3.0))
|
self.play(ShimmerIn(CompoundMobject(*mob_list), run_time = 3.0))
|
||||||
|
|
||||||
|
|
||||||
class AllValuesBetween1And2(Scene):
|
class SupposeThereIsASavant(Scene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
words = "Suppose there is a musical savant " + \
|
||||||
|
"who find pleasure in all pairs of " + \
|
||||||
|
"notes whose frequencies have a rational ratio"
|
||||||
|
words = text_mobject(words.split(" ")).split()
|
||||||
|
words[4].highlight()
|
||||||
|
words[5].highlight()
|
||||||
|
for word in words:
|
||||||
|
self.add(word)
|
||||||
|
self.dither(0.2)
|
||||||
|
|
||||||
|
class AllValuesBetween1And2(NumberLineScene):
|
||||||
|
def construct(self):
|
||||||
|
NumberLineScene.construct(self)
|
||||||
irrational = 1.2020569031595942
|
irrational = 1.2020569031595942
|
||||||
cont_frac = [1, 4, 1, 18, 1, 1, 1, 4, 1, 9, 9, 2, 1, 1, 1, 2]
|
cont_frac = [1, 4, 1, 18, 1, 1, 1, 4, 1, 9, 9, 2, 1, 1, 1, 2]
|
||||||
number_line = NumberLine(interval_size = 1).add_numbers()
|
one, two, irr = map(
|
||||||
one, two = 2*RIGHT, 4*RIGHT
|
self.number_line.number_to_point,
|
||||||
|
[1, 2, irrational]
|
||||||
|
)
|
||||||
top_arrow = Arrow(one+UP, one)
|
top_arrow = Arrow(one+UP, one)
|
||||||
bot_arrow = Arrow(2*irrational*RIGHT+DOWN, 2*irrational*RIGHT)
|
bot_arrow = Arrow(irr+2*DOWN, irr)
|
||||||
r = tex_mobject("r").next_to(top_arrow, UP)
|
r = tex_mobject("r").next_to(top_arrow, UP)
|
||||||
irr_mob = tex_mobject(str(irrational)).next_to(bot_arrow, DOWN)
|
irr_mob = tex_mobject(str(irrational)+"\\dots").next_to(bot_arrow, DOWN)
|
||||||
|
|
||||||
approximations = [
|
approximations = [
|
||||||
continued_fraction(cont_frac[:k])
|
continued_fraction(cont_frac[:k])
|
||||||
for k in range(1, len(cont_frac))
|
for k in range(1, len(cont_frac))[:8]
|
||||||
]
|
]
|
||||||
approx_mobs = [fraction_mobject(a) for a in approximations]
|
|
||||||
|
|
||||||
self.add(number_line)
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"run_time" : 3.0,
|
"run_time" : 3.0,
|
||||||
"alpha_func" : there_and_back
|
"alpha_func" : there_and_back
|
||||||
}
|
}
|
||||||
self.play(*[
|
self.play(*[
|
||||||
ApplyMethod(mob.shift, 2*RIGHT, **kwargs)
|
ApplyMethod(mob.shift, RIGHT, **kwargs)
|
||||||
for mob in r, top_arrow
|
for mob in r, top_arrow
|
||||||
])
|
])
|
||||||
|
self.dither()
|
||||||
self.remove(r, top_arrow)
|
self.remove(r, top_arrow)
|
||||||
self.play(
|
self.play(
|
||||||
ShimmerIn(irr_mob),
|
ShimmerIn(irr_mob),
|
||||||
ShowCreation(bot_arrow)
|
ShowCreation(bot_arrow)
|
||||||
)
|
)
|
||||||
|
self.dither()
|
||||||
|
self.add(irr_mob, bot_arrow)
|
||||||
|
|
||||||
frac_mob = Point(top_arrow.get_top())
|
frac_mob = Point(top_arrow.get_top())
|
||||||
|
max_num_zooms = 4
|
||||||
|
num_zooms = 0
|
||||||
for approx in approximations:
|
for approx in approximations:
|
||||||
point = 2*float(approx)*RIGHT
|
point = self.number_line.number_to_point(approx)
|
||||||
new_arrow = Arrow(point+UP, point)
|
new_arrow = Arrow(point+UP, point)
|
||||||
mob = fraction_mobject(approx).next_to(new_arrow, UP)
|
mob = fraction_mobject(approx).next_to(new_arrow, UP)
|
||||||
self.play(
|
self.play(
|
||||||
Transform(top_arrow, new_arrow),
|
Transform(top_arrow, new_arrow),
|
||||||
Transform(frac_mob, mob),
|
Transform(frac_mob, mob),
|
||||||
run_time = 0.2
|
run_time = 0.5
|
||||||
)
|
)
|
||||||
self.dither(0.5)
|
self.dither(0.5)
|
||||||
|
points = map(self.number_line.number_to_point, [approx, irrational])
|
||||||
|
distance = np.linalg.norm(points[1]-points[0])
|
||||||
|
if distance < 0.3*SPACE_WIDTH and num_zooms < max_num_zooms:
|
||||||
|
num_zooms += 1
|
||||||
|
new_distance = 0.75*SPACE_WIDTH
|
||||||
|
self.zoom_in_on(irrational, new_distance/distance)
|
||||||
|
for mob in irr_mob, bot_arrow:
|
||||||
|
mob.shift(mob.get_center()[0]*LEFT)
|
||||||
|
self.dither(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
class CoveringSetsWithOpenIntervals(IntervalScene):
|
||||||
class SampleIntervalScene(IntervalScene):
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
IntervalScene.construct(self)
|
IntervalScene.construct(self)
|
||||||
self.cover_fractions()
|
dots = CompoundMobject(*[
|
||||||
|
Dot().shift(self.number_line.number_to_point(num)+UP)
|
||||||
|
for num in set([0.2, 0.25, 0.45, 0.6, 0.65])
|
||||||
|
])
|
||||||
|
theorems = [
|
||||||
|
text_mobject(words).shift(UP)
|
||||||
|
for words in [
|
||||||
|
"Heine-Borel Theorem",
|
||||||
|
"Lebesgue's Number Lemma",
|
||||||
|
"Vitali Covering Lemma",
|
||||||
|
"Besicovitch Covering Theorem",
|
||||||
|
"$\\vdots$"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
self.add(dots)
|
||||||
|
self.play(DelayByOrder(ApplyMethod(dots.shift, DOWN, run_time = 2)))
|
||||||
|
self.dither()
|
||||||
|
for center in 0.225, 0.475, 0.625:
|
||||||
|
self.add_open_interval(center, 0.1, run_time = 1.0)
|
||||||
|
self.dither()
|
||||||
|
for x in range(2*len(theorems)):
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(th.shift, UP, alpha_func = None)
|
||||||
|
for th in theorems[:x+1]
|
||||||
|
])
|
||||||
|
|
||||||
|
class DefineOpenInterval(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
open_interval, line = self.add_open_interval(0.5, 0.05, run_time = 1.0)
|
||||||
|
left, right = open_interval.get_left(), open_interval.get_right()
|
||||||
|
a, less_than1, x, less_than2, b = \
|
||||||
|
tex_mobject(["a", "<", "x", "<", "b"]).shift(UP).split()
|
||||||
|
left_arrow = Arrow(a, left)
|
||||||
|
right_arrow = Arrow(b, right)
|
||||||
|
|
||||||
|
self.play(*[ShimmerIn(mob) for mob in a, less_than1, x])
|
||||||
|
self.play(ShowCreation(left_arrow))
|
||||||
|
self.dither()
|
||||||
|
self.play(*[ShimmerIn(mob) for mob in less_than2, b])
|
||||||
|
self.play(ShowCreation(right_arrow))
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
@ -707,6 +827,446 @@ class ShowAllFractions(IntervalScene):
|
||||||
remove_as_you_go = False,
|
remove_as_you_go = False,
|
||||||
pause_time = 0.3
|
pause_time = 0.3
|
||||||
)
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(*[
|
||||||
|
CounterclockwiseTransform(mob, Point(mob.get_center()))
|
||||||
|
for mob in self.mobjects
|
||||||
|
if isinstance(mob, ImageMobject)
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class NaiveFractionCover(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
self.add_fraction_ticks(100)
|
||||||
|
self.add_fraction_ticks(run_time = 5.0)
|
||||||
|
last_interval = None
|
||||||
|
centers = np.arange(0, 1.1, .1)
|
||||||
|
random.shuffle(centers)
|
||||||
|
for num, count in zip(centers, it.count()):
|
||||||
|
if count == 0:
|
||||||
|
kwargs = {
|
||||||
|
"run_time" : 1.0,
|
||||||
|
"alpha_func" : rush_into
|
||||||
|
}
|
||||||
|
elif count == 10:
|
||||||
|
kwargs = {
|
||||||
|
"run_time" : 1.0,
|
||||||
|
"alpha_func" : rush_from
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
kwargs = {
|
||||||
|
"run_time" : 0.25,
|
||||||
|
"alpha_func" : None
|
||||||
|
}
|
||||||
|
open_interval, line = self.add_open_interval(num, 0.1)
|
||||||
|
open_interval.shift(UP)
|
||||||
|
self.remove(line)
|
||||||
|
anims = [ApplyMethod(open_interval.shift, DOWN, **kwargs)]
|
||||||
|
last_interval = open_interval
|
||||||
|
self.play(*anims)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class CoverFractionsWithWholeInterval(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
self.add_fraction_ticks()
|
||||||
|
self.dither()
|
||||||
|
self.add_open_interval(0.5, 1, color = "red", run_time = 2.0)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class SumOfIntervalsMustBeLessThan1(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
self.add_fraction_ticks()
|
||||||
|
anims = []
|
||||||
|
last_plus = Point((SPACE_WIDTH-0.5)*LEFT+2*UP)
|
||||||
|
for num in np.arange(0, 1.1, .1):
|
||||||
|
open_interval, line = self.add_open_interval(num, 0.1)
|
||||||
|
self.remove(line)
|
||||||
|
int_copy = deepcopy(open_interval)
|
||||||
|
int_copy.scale(0.6).next_to(last_plus, buff = 0.1)
|
||||||
|
last_plus = tex_mobject("+").scale(0.3)
|
||||||
|
last_plus.next_to(int_copy, buff = 0.1)
|
||||||
|
anims.append(Transform(open_interval, int_copy))
|
||||||
|
if num < 1.0:
|
||||||
|
anims.append(FadeIn(last_plus))
|
||||||
|
less_than1 = tex_mobject("<1").scale(0.5)
|
||||||
|
less_than1.next_to(int_copy)
|
||||||
|
dots = tex_mobject("\\dots").replace(int_copy)
|
||||||
|
words = text_mobject("Use infinitely many intervals")
|
||||||
|
words.shift(UP)
|
||||||
|
|
||||||
|
self.dither()
|
||||||
|
self.play(*anims)
|
||||||
|
self.play(ShimmerIn(less_than1))
|
||||||
|
self.dither()
|
||||||
|
self.play(Transform(anims[-1].mobject, dots))
|
||||||
|
self.play(ShimmerIn(words))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class RationalsAreDense(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
words = text_mobject(["Rationals are", "\\emph{dense}", "in the reals"])
|
||||||
|
words.shift(2*UP)
|
||||||
|
words = words.split()
|
||||||
|
words[1].highlight()
|
||||||
|
|
||||||
|
self.add(words[0])
|
||||||
|
self.play(ShimmerIn(words[1]))
|
||||||
|
self.add(words[2])
|
||||||
|
self.dither()
|
||||||
|
ticks = self.add_fraction_ticks(run_time = 5.0)
|
||||||
|
self.dither()
|
||||||
|
for center, width in [(0.5, 0.1), (0.2, 0.05), (0.7, 0.01)]:
|
||||||
|
oi, line = self.add_open_interval(center, width, run_time = 1)
|
||||||
|
self.remove(line)
|
||||||
|
self.dither()
|
||||||
|
for compound in oi, ticks:
|
||||||
|
self.remove(compound)
|
||||||
|
self.add(*compound.split())
|
||||||
|
self.zoom_in_on(0.7, 100)
|
||||||
|
self.play(ShowCreation(ticks, run_time = 2.0))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class HowCanYouNotCoverEntireInterval(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
words = text_mobject("""
|
||||||
|
In case you are wondering, it is indeed true
|
||||||
|
that if you cover all real numbers between 0
|
||||||
|
and 1 with a set of open intervals, the sum
|
||||||
|
of the lengths of those intervals must be at
|
||||||
|
least 1. While intuitive, this is not actually
|
||||||
|
as obvious as it might seem, just try to prove
|
||||||
|
it for yourself!
|
||||||
|
""")
|
||||||
|
words.scale(0.5).to_corner(UP+RIGHT)
|
||||||
|
ticks = self.add_fraction_ticks()
|
||||||
|
left = self.number_line.number_to_point(0)
|
||||||
|
right = self.number_line.number_to_point(1)
|
||||||
|
full_line = Line(left, right)
|
||||||
|
full_line.highlight("red")
|
||||||
|
# dot = Dot().shift(left).highlight("red")
|
||||||
|
# point = Point(left).highlight("red")
|
||||||
|
intervals = []
|
||||||
|
for num in np.arange(0, 1.1, .1):
|
||||||
|
open_interval, line = self.add_open_interval(num, 0.1)
|
||||||
|
self.remove(line)
|
||||||
|
intervals.append(open_interval)
|
||||||
|
self.dither()
|
||||||
|
self.remove(ticks)
|
||||||
|
self.play(*[
|
||||||
|
Transform(tick, full_line)
|
||||||
|
for tick in ticks.split()
|
||||||
|
])
|
||||||
|
# self.play(DelayByOrder(FadeToColor(full_line, "red")))
|
||||||
|
self.play(ShimmerIn(words))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class StepsToSolution(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
self.spacing = 0.7
|
||||||
|
steps = map(text_mobject, [
|
||||||
|
"Enumerate all rationals in (0, 1)",
|
||||||
|
"Assign one interval to each rational",
|
||||||
|
"Choose sum of the form $\\sum_{n=1}^\\infty a_n = 1$",
|
||||||
|
"Pick any $\\epsilon$ such that $0 < \\epsilon < 1$",
|
||||||
|
"Stretch the $n$th interval to have length $\\epsilon/2^n$",
|
||||||
|
])
|
||||||
|
for step in steps:
|
||||||
|
step.shift(DOWN)
|
||||||
|
for step, count in zip(steps, it.count()):
|
||||||
|
self.add(step)
|
||||||
|
self.dither()
|
||||||
|
if count == 0:
|
||||||
|
self.enumerate_rationals()
|
||||||
|
elif count == 1:
|
||||||
|
self.assign_intervals_to_rationals()
|
||||||
|
elif count == 2:
|
||||||
|
self.add_terms()
|
||||||
|
elif count == 3:
|
||||||
|
self.multiply_by_epsilon()
|
||||||
|
elif count == 4:
|
||||||
|
self.stretch_intervals()
|
||||||
|
self.remove(step)
|
||||||
|
|
||||||
|
def enumerate_rationals(self):
|
||||||
|
ticks = self.add_fraction_ticks(run_time = 2.0)
|
||||||
|
anims = []
|
||||||
|
commas = Mobject()
|
||||||
|
denom_to_mobs = {}
|
||||||
|
for frac, count in zip(rationals(), range(1,28)):
|
||||||
|
mob, tick = self.add_fraction(frac, shrink = True)
|
||||||
|
self.dither(0.1)
|
||||||
|
self.remove(tick)
|
||||||
|
if frac.denominator not in denom_to_mobs:
|
||||||
|
denom_to_mobs[frac.denominator] = []
|
||||||
|
denom_to_mobs[frac.denominator].append(mob)
|
||||||
|
mob_copy = deepcopy(mob).center()
|
||||||
|
mob_copy.shift((2.4-mob_copy.get_bottom()[1])*UP)
|
||||||
|
mob_copy.shift((-SPACE_WIDTH+self.spacing*count)*RIGHT)
|
||||||
|
comma = text_mobject(",").next_to(mob_copy, buff = 0.1, aligned_edge = DOWN)
|
||||||
|
anims.append(Transform(mob, mob_copy))
|
||||||
|
commas.add(comma)
|
||||||
|
anims.append(ShimmerIn(commas))
|
||||||
|
new_ticks = []
|
||||||
|
for tick, count in zip(ticks.split(), it.count(1)):
|
||||||
|
tick_copy = deepcopy(tick).center().shift(1.6*UP)
|
||||||
|
tick_copy.shift((-SPACE_WIDTH+self.spacing*count)*RIGHT)
|
||||||
|
new_ticks.append(tick_copy)
|
||||||
|
new_ticks = CompoundMobject(*new_ticks)
|
||||||
|
anims.append(DelayByOrder(Transform(ticks, new_ticks)))
|
||||||
|
self.dither()
|
||||||
|
self.play(*anims)
|
||||||
|
self.dither()
|
||||||
|
for denom in range(2, 10):
|
||||||
|
for mob in denom_to_mobs[denom]:
|
||||||
|
mob.highlight("green")
|
||||||
|
self.dither()
|
||||||
|
for mob in denom_to_mobs[denom]:
|
||||||
|
mob.to_original_color()
|
||||||
|
self.ticks = ticks.split()[:20]
|
||||||
|
|
||||||
|
def assign_intervals_to_rationals(self):
|
||||||
|
anims = []
|
||||||
|
for tick in self.ticks:
|
||||||
|
interval = OpenInterval(tick.get_center(), self.spacing/2)
|
||||||
|
squished = deepcopy(interval).stretch_to_fit_width(0)
|
||||||
|
anims.append(Transform(squished, interval))
|
||||||
|
self.play(*anims)
|
||||||
|
self.dither()
|
||||||
|
self.intervals = [a.mobject for a in anims]
|
||||||
|
kwargs = {
|
||||||
|
"run_time" : 2.0,
|
||||||
|
"alpha_func" : there_and_back
|
||||||
|
}
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(mob.scale_in_place, 0.5*random.random(), **kwargs)
|
||||||
|
for mob in self.intervals
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def add_terms(self):
|
||||||
|
self.ones = []
|
||||||
|
scale_val = 0.3
|
||||||
|
for count in range(1, 10):
|
||||||
|
frac_bottom = tex_mobject("\\over %d"%(2**count))
|
||||||
|
frac_bottom.scale(scale_val)
|
||||||
|
frac_bottom.shift(0.5*UP + (-SPACE_WIDTH+self.spacing*count)*RIGHT)
|
||||||
|
one = tex_mobject("1").scale(scale_val)
|
||||||
|
one.next_to(frac_bottom, UP, buff = 0.1)
|
||||||
|
self.ones.append(one)
|
||||||
|
plus = tex_mobject("+").scale(scale_val)
|
||||||
|
plus.next_to(frac_bottom, buff = self.spacing/4)
|
||||||
|
self.add(frac_bottom, one, plus)
|
||||||
|
self.dither(0.2)
|
||||||
|
dots = tex_mobject("\\dots").scale(scale_val).next_to(plus)
|
||||||
|
arrow = Arrow(ORIGIN, RIGHT).next_to(dots)
|
||||||
|
one = tex_mobject("1").next_to(arrow)
|
||||||
|
self.ones.append(one)
|
||||||
|
self.play(*[ShowCreation(mob) for mob in dots, arrow, one])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def multiply_by_epsilon(self):
|
||||||
|
self.play(*[
|
||||||
|
CounterclockwiseTransform(
|
||||||
|
one,
|
||||||
|
tex_mobject("\\epsilon").replace(one)
|
||||||
|
)
|
||||||
|
for one in self.ones
|
||||||
|
])
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
def stretch_intervals(self):
|
||||||
|
for interval, count in zip(self.intervals, it.count(1)):
|
||||||
|
self.play(
|
||||||
|
ApplyMethod(interval.scale_in_place, 1.0/(count**2)),
|
||||||
|
run_time = 1.0/count
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class OurSumCanBeArbitrarilySmall(Scene):
|
||||||
|
def construct(self):
|
||||||
|
step_size = 0.01
|
||||||
|
epsilon = tex_mobject("\\epsilon")
|
||||||
|
equals = tex_mobject("=").next_to(epsilon)
|
||||||
|
self.add(epsilon, equals)
|
||||||
|
for num in np.arange(1, 0, -step_size):
|
||||||
|
parts = map(tex_mobject, str(num))
|
||||||
|
parts[0].next_to(equals)
|
||||||
|
for i in range(1, len(parts)):
|
||||||
|
parts[i].next_to(parts[i-1], buff = 0.1, aligned_edge = DOWN)
|
||||||
|
self.add(*parts)
|
||||||
|
self.dither(0.05)
|
||||||
|
self.remove(*parts)
|
||||||
|
self.dither()
|
||||||
|
self.clear()
|
||||||
|
words = text_mobject([
|
||||||
|
"Not only can the sum be $< 1$,\\\\",
|
||||||
|
"it can be \\emph{arbitrarily small} !"
|
||||||
|
]).split()
|
||||||
|
self.add(words[0])
|
||||||
|
self.dither()
|
||||||
|
self.play(ShimmerIn(words[1].highlight()))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class StillFeelsCounterintuitive(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
ticks = self.add_fraction_ticks(run_time = 1.0)
|
||||||
|
epsilon, equals, num = map(tex_mobject, ["\\epsilon", "=", "0.3"])
|
||||||
|
epsilon.shift(2*UP)
|
||||||
|
equals.next_to(epsilon)
|
||||||
|
num.next_to(equals)
|
||||||
|
self.add(epsilon, equals, num)
|
||||||
|
self.cover_fractions()
|
||||||
|
self.remove(ticks)
|
||||||
|
self.add(*ticks.split())
|
||||||
|
self.zoom_in_on(np.sqrt(2)/2, 100)
|
||||||
|
self.play(ShowCreation(ticks))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
class ZoomInOnSqrt2Over2(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
epsilon, equals, num = map(tex_mobject, ["\\epsilon", "=", "0.3"])
|
||||||
|
equals.next_to(epsilon)
|
||||||
|
num.next_to(equals)
|
||||||
|
self.add(CompoundMobject(epsilon, equals, num).center().shift(2*UP))
|
||||||
|
intervals, lines = self.cover_fractions()
|
||||||
|
self.remove(*lines)
|
||||||
|
irr = tex_mobject("\\frac{\\sqrt{2}}{2}")
|
||||||
|
point = self.number_line.number_to_point(np.sqrt(2)/2)
|
||||||
|
arrow = Arrow(point+UP, point)
|
||||||
|
irr.next_to(arrow, UP)
|
||||||
|
self.play(ShimmerIn(irr), ShowCreation(arrow))
|
||||||
|
for count in range(4):
|
||||||
|
self.remove(*intervals)
|
||||||
|
self.remove(*lines)
|
||||||
|
self.zoom_in_on(np.sqrt(2)/2, 20)
|
||||||
|
for mob in irr, arrow:
|
||||||
|
mob.shift(mob.get_center()[0]*LEFT)
|
||||||
|
intervals, lines = self.cover_fractions()
|
||||||
|
|
||||||
|
class NotCoveredMeansCacophonous(Scene):
|
||||||
|
def construct(self):
|
||||||
|
statement1 = text_mobject("$\\frac{\\sqrt{2}}{2}$ is not covered")
|
||||||
|
implies = tex_mobject("\\Rightarrow")
|
||||||
|
statement2 = text_mobject("Rationals which are close to $\\frac{\\sqrt{2}}{2}$ must have large denominators")
|
||||||
|
statement1.to_edge(LEFT)
|
||||||
|
implies.next_to(statement1)
|
||||||
|
statement2.next_to(implies)
|
||||||
|
implies.sort_points()
|
||||||
|
|
||||||
|
self.add(statement1)
|
||||||
|
self.dither()
|
||||||
|
self.play(ShowCreation(implies))
|
||||||
|
self.play(ShimmerIn(statement2))
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
class ShiftSetupByOne(IntervalScene):
|
||||||
|
def construct(self):
|
||||||
|
IntervalScene.construct(self)
|
||||||
|
new_interval = UnitInterval(
|
||||||
|
number_at_center = 1.5,
|
||||||
|
leftmost_tick = 1,
|
||||||
|
numbers_with_elongated_ticks = [1, 2],
|
||||||
|
)
|
||||||
|
new_interval.add_numbers(1, 2)
|
||||||
|
side_shift_val = self.number_line.number_to_point(1)
|
||||||
|
side_shift_val -= new_interval.number_to_point(1)
|
||||||
|
new_interval.shift(side_shift_val)
|
||||||
|
self.add(new_interval)
|
||||||
|
self.number_line.add_numbers(0)
|
||||||
|
self.remove(*self.number_mobs)
|
||||||
|
epsilon_mob = tex_mobject("\\epsilon = 0.01").to_edge(UP)
|
||||||
|
self.add(epsilon_mob)
|
||||||
|
fraction_ticks = self.add_fraction_ticks()
|
||||||
|
self.remove(fraction_ticks)
|
||||||
|
all_intervals, all_lines = self.cover_fractions(
|
||||||
|
epsilon = 0.01,
|
||||||
|
num_fractions = 150,
|
||||||
|
run_time_per_interval = 0,
|
||||||
|
)
|
||||||
|
self.remove(*all_intervals+all_lines)
|
||||||
|
|
||||||
|
intervals, lines = self.cover_fractions(epsilon = 0.01)
|
||||||
|
self.dither()
|
||||||
|
mobs_shifts = [
|
||||||
|
(intervals+lines, UP),
|
||||||
|
([self.number_line, new_interval], side_shift_val*LEFT),
|
||||||
|
(intervals+lines, DOWN)
|
||||||
|
]
|
||||||
|
for mobs, shift_val in mobs_shifts:
|
||||||
|
self.play(*[
|
||||||
|
ApplyMethod(mob.shift, shift_val)
|
||||||
|
for mob in mobs
|
||||||
|
])
|
||||||
|
self.number_line = new_interval
|
||||||
|
self.dither()
|
||||||
|
words = text_mobject(
|
||||||
|
"Almost all the covered numbers are harmonious!",
|
||||||
|
size = "\\small"
|
||||||
|
).shift(2*UP)
|
||||||
|
self.play(ShimmerIn(words))
|
||||||
|
self.dither()
|
||||||
|
for num in [7, 5]:
|
||||||
|
point = self.number_line.number_to_point(2**(num/12.))
|
||||||
|
arrow = Arrow(point+DOWN, point)
|
||||||
|
mob = tex_mobject(
|
||||||
|
"2^{\\left(\\frac{%d}{12}\\right)}"%num
|
||||||
|
).next_to(arrow, DOWN)
|
||||||
|
self.play(ShimmerIn(mob), ShowCreation(arrow))
|
||||||
|
self.dither()
|
||||||
|
self.remove(mob, arrow)
|
||||||
|
self.remove(words)
|
||||||
|
words = text_mobject(
|
||||||
|
"Cacophonous covered numbers:",
|
||||||
|
size = "\\small"
|
||||||
|
)
|
||||||
|
words.shift(2*UP)
|
||||||
|
answer1 = text_mobject("Complicated rationals,", size = "\\small")
|
||||||
|
answer2 = text_mobject(
|
||||||
|
"real numbers \\emph{very very very} close to them",
|
||||||
|
size = "\\small"
|
||||||
|
)
|
||||||
|
compound = CompoundMobject(answer1, answer2.next_to(answer1))
|
||||||
|
compound.next_to(words, DOWN)
|
||||||
|
answer1, answer2 = compound.split()
|
||||||
|
|
||||||
|
self.add(words)
|
||||||
|
self.dither()
|
||||||
|
self.remove(*intervals+lines)
|
||||||
|
self.add(answer1)
|
||||||
|
self.play(ShowCreation(fraction_ticks, run_time = 5.0))
|
||||||
|
self.add(answer2)
|
||||||
|
self.dither()
|
||||||
|
self.remove(words, answer1, answer2)
|
||||||
|
words = text_mobject([
|
||||||
|
"To a", "savant,", "harmonious numbers could be ",
|
||||||
|
"\\emph{precisely}", "those 1\\% covered by the intervals"
|
||||||
|
]).shift(2*UP)
|
||||||
|
words = words.split()
|
||||||
|
words[1].highlight()
|
||||||
|
words[3].highlight()
|
||||||
|
self.add(*words)
|
||||||
|
for interval, frac in zip(all_intervals, rationals()):
|
||||||
|
interval.scale_in_place(2.0/frac.denominator)
|
||||||
|
self.add(interval)
|
||||||
|
self.dither(0.1)
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Add table
Reference in a new issue