Files
manim/old_projects/alt_calc.py
2019-05-02 20:36:14 -07:00

3775 lines
118 KiB
Python

# -*- coding: utf-8 -*-
from manimlib.imports import *
def apply_function_to_center(point_func, mobject):
mobject.apply_function_to_position(point_func)
def apply_function_to_submobjects(point_func, mobject):
mobject.apply_function_to_submobject_positions(point_func)
def apply_function_to_points(point_func, mobject):
mobject.apply_function(point_func)
def get_nested_one_plus_one_over_x(n_terms, bottom_term="x"):
tex = "1+ {1 \\over" * n_terms + bottom_term + "}" * n_terms
return TexMobject(tex, substrings_to_isolate=["1", "\\over", bottom_term])
def get_phi_continued_fraction(n_terms):
return get_nested_one_plus_one_over_x(n_terms, bottom_term="1+\\cdots")
def get_nested_f(n_terms, arg="x"):
terms = ["f("] * n_terms + [arg] + [")"] * n_terms
return TexMobject(*terms)
# Scene types
class NumberlineTransformationScene(ZoomedScene):
CONFIG = {
"input_line_zero_point": 0.5 * UP + (FRAME_X_RADIUS - 1) * LEFT,
"output_line_zero_point": 2 * DOWN + (FRAME_X_RADIUS - 1) * LEFT,
"number_line_config": {
"include_numbers": True,
"x_min": -3.5,
"x_max": 3.5,
"unit_size": 2,
},
# These would override number_line_config
"input_line_config": {
"color": BLUE,
},
"output_line_config": {},
"num_inserted_number_line_curves": 20,
"default_delta_x": 0.1,
"default_sample_dot_radius": 0.07,
"default_sample_dot_colors": [RED, YELLOW],
"default_mapping_animation_config": {
"run_time": 3,
# "path_arc": 30 * DEGREES,
},
"local_coordinate_num_decimal_places": 2,
"zoom_factor": 0.1,
"zoomed_display_height": 2.5,
"zoomed_display_corner_buff": MED_SMALL_BUFF,
"mini_line_scale_factor": 2,
"default_coordinate_value_dx": 0.05,
"zoomed_camera_background_rectangle_fill_opacity": 1.0,
}
def setup(self):
ZoomedScene.setup(self)
self.setup_number_lines()
self.setup_titles()
self.setup_zoomed_camera_background_rectangle()
def setup_number_lines(self):
number_lines = self.number_lines = VGroup()
added_configs = (self.input_line_config, self.output_line_config)
zero_opints = (self.input_line_zero_point, self.output_line_zero_point)
for added_config, zero_point in zip(added_configs, zero_opints):
full_config = dict(self.number_line_config)
full_config.update(added_config)
number_line = NumberLine(**full_config)
number_line.insert_n_curves(
self.num_inserted_number_line_curves
)
number_line.shift(zero_point - number_line.number_to_point(0))
number_lines.add(number_line)
self.input_line, self.output_line = number_lines
self.add(number_lines)
def setup_titles(self):
input_title, output_title = self.titles = VGroup(*[
TextMobject(word)
for word in ("Inputs", "Outputs")
])
vects = [UP, DOWN]
for title, line, vect in zip(self.titles, self.number_lines, vects):
title.next_to(line, vect, aligned_edge=LEFT)
title.shift_onto_screen()
self.add(self.titles)
def setup_zoomed_camera_background_rectangle(self):
frame = self.zoomed_camera.frame
frame.next_to(self.camera.frame, UL)
self.zoomed_camera_background_rectangle = BackgroundRectangle(
frame, fill_opacity=self.zoomed_camera_background_rectangle_fill_opacity
)
self.zoomed_camera_background_rectangle_anim = UpdateFromFunc(
self.zoomed_camera_background_rectangle,
lambda m: m.replace(frame, stretch=True)
)
self.zoomed_camera_background_rectangle_group = VGroup(
self.zoomed_camera_background_rectangle,
)
def get_sample_input_points(self, x_min=None, x_max=None, delta_x=None):
x_min = x_min or self.input_line.x_min
x_max = x_max or self.input_line.x_max
delta_x = delta_x or self.default_delta_x
return [
self.get_input_point(x)
for x in np.arange(x_min, x_max + delta_x, delta_x)
]
def get_sample_dots(self, x_min=None, x_max=None,
delta_x=None, dot_radius=None, colors=None):
dot_radius = dot_radius or self.default_sample_dot_radius
colors = colors or self.default_sample_dot_colors
dots = VGroup(*[
Dot(point, radius=dot_radius)
for point in self.get_sample_input_points(x_min, x_max, delta_x)
])
dots.set_color_by_gradient(*colors)
return dots
def get_local_sample_dots(self, x, sample_radius=None, **kwargs):
zoom_factor = self.get_zoom_factor()
delta_x = kwargs.get("delta_x", self.default_delta_x * zoom_factor)
dot_radius = kwargs.get("dot_radius", self.default_sample_dot_radius * zoom_factor)
if sample_radius is None:
unrounded_radius = self.zoomed_camera.frame.get_width() / 2
sample_radius = int(unrounded_radius / delta_x) * delta_x
config = {
"x_min": x - sample_radius,
"x_max": x + sample_radius,
"delta_x": delta_x,
"dot_radius": dot_radius,
}
config.update(kwargs)
return self.get_sample_dots(**config)
def add_sample_dot_ghosts(self, sample_dots, fade_factor=0.5):
self.sample_dot_ghosts = sample_dots.copy()
self.sample_dot_ghosts.fade(fade_factor)
self.add(self.sample_dot_ghosts, sample_dots)
def get_local_coordinate_values(self, x, dx=None, n_neighbors=1):
dx = dx or self.default_coordinate_value_dx
return [
x + n * dx
for n in range(-n_neighbors, n_neighbors + 1)
]
# Mapping animations
def get_mapping_animation(self, func, mobject,
how_to_apply_func=apply_function_to_center,
**kwargs):
anim_config = dict(self.default_mapping_animation_config)
anim_config.update(kwargs)
point_func = self.number_func_to_point_func(func)
mobject.generate_target(use_deepcopy=True)
how_to_apply_func(point_func, mobject.target)
return MoveToTarget(mobject, **anim_config)
def get_line_mapping_animation(self, func, **kwargs):
input_line_copy = self.input_line.deepcopy()
self.moving_input_line = input_line_copy
input_line_copy.remove(input_line_copy.numbers)
# input_line_copy.set_stroke(width=2)
input_line_copy.insert_n_curves(
self.num_inserted_number_line_curves
)
return AnimationGroup(
self.get_mapping_animation(
func, input_line_copy,
apply_function_to_points
),
self.get_mapping_animation(
func, input_line_copy.tick_marks,
apply_function_to_submobjects
),
)
def get_sample_dots_mapping_animation(self, func, dots, **kwargs):
return self.get_mapping_animation(
func, dots, how_to_apply_func=apply_function_to_submobjects
)
def get_zoomed_camera_frame_mapping_animation(self, func, x=None, **kwargs):
frame = self.zoomed_camera.frame
if x is None:
point = frame.get_center()
else:
point = self.get_input_point(x)
point_mob = VectorizedPoint(point)
return AnimationGroup(
self.get_mapping_animation(func, point_mob),
UpdateFromFunc(frame, lambda m: m.move_to(point_mob)),
)
def apply_function(self, func,
apply_function_to_number_line=True,
sample_dots=None,
local_sample_dots=None,
target_coordinate_values=None,
added_anims=None,
**kwargs
):
zcbr_group = self.zoomed_camera_background_rectangle_group
zcbr_anim = self.zoomed_camera_background_rectangle_anim
frame = self.zoomed_camera.frame
anims = []
if apply_function_to_number_line:
anims.append(self.get_line_mapping_animation(func))
if hasattr(self, "mini_line"): # Test for if mini_line is in self?
anims.append(self.get_mapping_animation(
func, self.mini_line,
how_to_apply_func=apply_function_to_center
))
if sample_dots:
anims.append(
self.get_sample_dots_mapping_animation(func, sample_dots)
)
if self.zoom_activated:
zoom_anim = self.get_zoomed_camera_frame_mapping_animation(func)
anims.append(zoom_anim)
anims.append(zcbr_anim)
zoom_anim.update(1)
target_mini_line = Line(frame.get_left(), frame.get_right())
target_mini_line.scale(self.mini_line_scale_factor)
target_mini_line.match_style(self.output_line)
zoom_anim.update(0)
zcbr_group.submobjects.insert(1, target_mini_line)
if target_coordinate_values:
coordinates = self.get_local_coordinates(
self.output_line,
*target_coordinate_values
)
anims.append(FadeIn(coordinates))
zcbr_group.add(coordinates)
self.local_target_coordinates = coordinates
if local_sample_dots:
anims.append(
self.get_sample_dots_mapping_animation(func, local_sample_dots)
)
zcbr_group.add(local_sample_dots)
if added_anims:
anims += added_anims
anims.append(Animation(zcbr_group))
self.play(*anims, **kwargs)
# Zooming
def zoom_in_on_input(self, x,
local_sample_dots=None,
local_coordinate_values=None,
pop_out=True,
first_added_anims=None,
first_anim_kwargs=None,
second_added_anims=None,
second_anim_kwargs=None,
zoom_factor=None,
):
first_added_anims = first_added_anims or []
first_anim_kwargs = first_anim_kwargs or {}
second_added_anims = second_added_anims or []
second_anim_kwargs = second_anim_kwargs or {}
input_point = self.get_input_point(x)
# Decide how to move camera frame into place
frame = self.zoomed_camera.frame
frame.generate_target()
frame.target.move_to(input_point)
if zoom_factor:
frame.target.set_height(
self.zoomed_display.get_height() * zoom_factor
)
movement = MoveToTarget(frame)
zcbr = self.zoomed_camera_background_rectangle
zcbr_group = self.zoomed_camera_background_rectangle_group
zcbr_anim = self.zoomed_camera_background_rectangle_anim
anims = []
if self.zoom_activated:
anims.append(movement)
anims.append(zcbr_anim)
else:
movement.update(1)
zcbr_anim.update(1)
anims.append(self.get_zoom_in_animation())
anims.append(FadeIn(zcbr))
# Make sure frame is in final place
for anim in anims:
anim.update(1)
# Add miniature number_line
mini_line = self.mini_line = Line(frame.get_left(), frame.get_right())
mini_line.scale(self.mini_line_scale_factor)
mini_line.insert_n_curves(self.num_inserted_number_line_curves)
mini_line.match_style(self.input_line)
mini_line_copy = mini_line.copy()
zcbr_group.add(mini_line_copy, mini_line)
anims += [FadeIn(mini_line), FadeIn(mini_line_copy)]
# Add tiny coordinates
if local_coordinate_values is None:
local_coordinate_values = [x]
local_coordinates = self.get_local_coordinates(
self.input_line,
*local_coordinate_values
)
anims.append(FadeIn(local_coordinates))
zcbr_group.add(local_coordinates)
self.local_coordinates = local_coordinates
# Add tiny dots
if local_sample_dots is not None:
anims.append(LaggedStartMap(GrowFromCenter, local_sample_dots))
zcbr_group.add(local_sample_dots)
if first_added_anims:
anims += first_added_anims
anims.append(Animation(zcbr_group))
if not pop_out:
self.activate_zooming(animate=False)
self.play(*anims, **first_anim_kwargs)
if not self.zoom_activated and pop_out:
self.activate_zooming(animate=False)
added_anims = second_added_anims or []
self.play(
self.get_zoomed_display_pop_out_animation(),
*added_anims,
**second_anim_kwargs
)
def get_local_coordinates(self, line, *x_values, **kwargs):
num_decimal_places = kwargs.get(
"num_decimal_places", self.local_coordinate_num_decimal_places
)
result = VGroup()
result.tick_marks = VGroup()
result.numbers = VGroup()
result.add(result.tick_marks, result.numbers)
for x in x_values:
tick_mark = Line(UP, DOWN)
tick_mark.set_height(
0.15 * self.zoomed_camera.frame.get_height()
)
tick_mark.move_to(line.number_to_point(x))
result.tick_marks.add(tick_mark)
number = DecimalNumber(x, num_decimal_places=num_decimal_places)
number.scale(self.get_zoom_factor())
number.scale(0.5) # To make it seem small
number.next_to(tick_mark, DOWN, buff=0.5 * number.get_height())
result.numbers.add(number)
return result
def get_mobjects_in_zoomed_camera(self, mobjects):
frame = self.zoomed_camera.frame
x_min = frame.get_left()[0]
x_max = frame.get_right()[0]
y_min = frame.get_bottom()[1]
y_max = frame.get_top()[1]
result = VGroup()
for mob in mobjects:
for point in mob.get_all_points():
if (x_min < point[0] < x_max) and (y_min < point[1] < y_max):
result.add(mob)
break
return result
# Helpers
def get_input_point(self, x):
return self.input_line.number_to_point(x)
def get_output_point(self, fx):
return self.output_line.number_to_point(fx)
def number_func_to_point_func(self, number_func):
input_line, output_line = self.number_lines
def point_func(point):
input_number = input_line.point_to_number(point)
output_number = number_func(input_number)
return output_line.number_to_point(output_number)
return point_func
class ExampleNumberlineTransformationScene(NumberlineTransformationScene):
CONFIG = {
"number_line_config": {
"x_min": 0,
"x_max": 5,
"unit_size": 2.0
},
"output_line_config": {
"x_max": 20,
},
}
def construct(self):
func = lambda x: x**2
x = 3
dx = 0.05
sample_dots = self.get_sample_dots()
local_sample_dots = self.get_local_sample_dots(x)
self.play(LaggedStartMap(GrowFromCenter, sample_dots))
self.zoom_in_on_input(
x,
local_sample_dots=local_sample_dots,
local_coordinate_values=[x - dx, x, x + dx],
)
self.wait()
self.apply_function(
func,
sample_dots=sample_dots,
local_sample_dots=local_sample_dots,
target_coordinate_values=[func(x) - dx, func(x), func(x) + dx],
)
self.wait()
# Scenes
class WriteOpeningWords(Scene):
def construct(self):
raw_string1 = "Dear calculus student,"
raw_string2 = "You're about to go through your first course. Like " + \
"any new topic, it will take some hard work to understand,"
words1, words2 = [
TextMobject("\\Large", *rs.split(" "))
for rs in (raw_string1, raw_string2)
]
words1.next_to(words2, UP, aligned_edge=LEFT, buff=LARGE_BUFF)
words = VGroup(*it.chain(words1, words2))
words.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)
words.to_edge(UP)
letter_wait = 0.05
word_wait = 2 * letter_wait
comma_wait = 5 * letter_wait
for word in words:
self.play(LaggedStartMap(
FadeIn, word,
run_time=len(word) * letter_wait,
lag_ratio=1.5 / len(word)
))
self.wait(word_wait)
if word.get_tex_string()[-1] == ",":
self.wait(comma_wait)
class StartingCalc101(PiCreatureScene):
CONFIG = {
"camera_config": {"background_opacity": 1},
"image_frame_width": 3.5,
"image_frame_height": 2.5,
}
def construct(self):
self.show_you()
self.show_images()
self.show_mystery_topic()
def show_you(self):
randy = self.pi_creature
title = self.title = Title("Calculus 101")
you = TextMobject("You")
arrow = Vector(DL, color=WHITE)
arrow.next_to(randy, UR)
you.next_to(arrow.get_start(), UP)
self.play(
Write(you),
GrowArrow(arrow),
randy.change, "erm", title
)
self.wait()
self.play(Write(title, run_time=1))
self.play(FadeOut(VGroup(arrow, you)))
def show_images(self):
randy = self.pi_creature
images = self.get_all_images()
modes = [
"pondering", # hard_work_image
"pondering", # neat_example_image
"hesitant", # not_so_neat_example_image
"hesitant", # physics_image
"horrified", # piles_of_formulas_image
"horrified", # getting_stuck_image
"thinking", # aha_image
"thinking", # graphical_intuition_image
]
for i, image, mode in zip(it.count(), images, modes):
anims = []
if hasattr(image, "fade_in_anim"):
anims.append(image.fade_in_anim)
anims.append(FadeIn(image.frame))
else:
anims.append(FadeIn(image))
if i >= 3:
image_to_fade_out = images[i - 3]
if hasattr(image_to_fade_out, "fade_out_anim"):
anims.append(image_to_fade_out.fade_out_anim)
else:
anims.append(FadeOut(image_to_fade_out))
if hasattr(image, "continual_animations"):
self.add(*image.continual_animations)
anims.append(ApplyMethod(randy.change, mode))
self.play(*anims)
self.wait()
if i >= 3:
if hasattr(image_to_fade_out, "continual_animations"):
self.remove(*image_to_fade_out.continual_animations)
self.remove(image_to_fade_out.frame)
self.wait(3)
self.remaining_images = images[-3:]
def show_mystery_topic(self):
images = self.remaining_images
randy = self.pi_creature
mystery_box = Rectangle(
width=self.image_frame_width,
height=self.image_frame_height,
stroke_color=YELLOW,
fill_color=DARK_GREY,
fill_opacity=0.5,
)
mystery_box.scale(1.5)
mystery_box.next_to(self.title, DOWN, MED_LARGE_BUFF)
rects = images[-1].rects.copy()
rects.center()
rects.set_height(FRAME_HEIGHT - 1)
# image = rects.get_image()
open_cv_image = cv2.imread(get_full_raster_image_path("alt_calc_hidden_image"))
blurry_iamge = cv2.blur(open_cv_image, (50, 50))
array = np.array(blurry_iamge)[:, :, ::-1]
im_mob = ImageMobject(array)
im_mob.replace(mystery_box, stretch=True)
mystery_box.add(im_mob)
q_marks = TexMobject("???").scale(3)
q_marks.space_out_submobjects(1.5)
q_marks.set_stroke(BLACK, 1)
q_marks.move_to(mystery_box)
mystery_box.add(q_marks)
for image in images:
if hasattr(image, "continual_animations"):
self.remove(*image.continual_animations)
self.play(
image.shift, DOWN,
image.fade, 1,
randy.change, "erm",
run_time=1.5
)
self.remove(image)
self.wait()
self.play(
FadeInFromDown(mystery_box),
randy.change, "confused"
)
self.wait(5)
# Helpers
def get_all_images(self):
# Images matched to narration's introductory list
images = VGroup(
self.get_hard_work_image(),
self.get_neat_example_image(),
self.get_not_so_neat_example_image(),
self.get_physics_image(),
self.get_piles_of_formulas_image(),
self.get_getting_stuck_image(),
self.get_aha_image(),
self.get_graphical_intuition_image(),
)
colors = color_gradient([BLUE, YELLOW], len(images))
for i, image, color in zip(it.count(), images, colors):
self.adjust_size(image)
frame = Rectangle(
width=self.image_frame_width,
height=self.image_frame_height,
color=color,
stroke_width=2,
)
frame.move_to(image)
image.frame = frame
image.add(frame)
image.next_to(self.title, DOWN)
alt_i = (i % 3) - 1
vect = (self.image_frame_width + LARGE_BUFF) * RIGHT
image.shift(alt_i * vect)
return images
def adjust_size(self, group):
group.set_width(min(
group.get_width(),
self.image_frame_width - 2 * MED_SMALL_BUFF
))
group.set_height(min(
group.get_height(),
self.image_frame_height - 2 * MED_SMALL_BUFF
))
return group
def get_hard_work_image(self):
new_randy = self.pi_creature.copy()
new_randy.change_mode("telepath")
bubble = new_randy.get_bubble(height=3.5, width=4)
bubble.add_content(TexMobject("\\frac{d}{dx}(\\sin(\\sqrt{x}))"))
bubble.add(bubble.content) # Remove?
return VGroup(new_randy, bubble)
def get_neat_example_image(self):
filled_circle = Circle(
stroke_width=0,
fill_color=BLUE_E,
fill_opacity=1
)
area = TexMobject("\\pi r^2")
area.move_to(filled_circle)
unfilled_circle = Circle(
stroke_width=3,
stroke_color=YELLOW,
fill_opacity=0,
)
unfilled_circle.next_to(filled_circle, RIGHT)
circles = VGroup(filled_circle, unfilled_circle)
circumference = TexMobject("2\\pi r")
circumference.move_to(unfilled_circle)
equation = TexMobject(
"{d (\\pi r^2) \\over dr} = 2\\pi r",
tex_to_color_map={
"\\pi r^2": BLUE_D,
"2\\pi r": YELLOW,
}
)
equation.next_to(circles, UP)
return VGroup(
filled_circle, area,
unfilled_circle, circumference,
equation
)
def get_not_so_neat_example_image(self):
return TexMobject("\\int x \\cos(x) \\, dx")
def get_physics_image(self):
t_max = 6.5
r = 0.2
spring = ParametricFunction(
lambda t: op.add(
r * (np.sin(TAU * t) * RIGHT + np.cos(TAU * t) * UP),
t * DOWN,
),
t_min=0, t_max=t_max,
color=WHITE,
stroke_width=2,
)
spring.color_using_background_image("grey_gradient")
weight = Square()
weight.set_stroke(width=0)
weight.set_fill(opacity=1)
weight.color_using_background_image("grey_gradient")
weight.set_height(0.4)
t_tracker = ValueTracker(0)
group = VGroup(spring, weight)
group.continual_animations = [
t_tracker.add_udpater(
lambda tracker, dt: tracker.set_value(
tracker.get_value() + dt
)
),
spring.add_updater(
lambda s: s.stretch_to_fit_height(
1.5 + 0.5 * np.cos(3 * t_tracker.get_value()),
about_edge=UP
)
),
weight.add_updater(
lambda w: w.move_to(spring.points[-1])
)
]
def update_group_style(alpha):
spring.set_stroke(width=2 * alpha)
weight.set_fill(opacity=alpha)
group.fade_in_anim = UpdateFromAlphaFunc(
group,
lambda g, a: update_group_style(a)
)
group.fade_out_anim = UpdateFromAlphaFunc(
group,
lambda g, a: update_group_style(1 - a)
)
return group
def get_piles_of_formulas_image(self):
return TexMobject("(f/g)' = \\frac{gf' - fg'}{g^2}")
def get_getting_stuck_image(self):
creature = self.pi_creature.copy()
creature.change_mode("angry")
equation = TexMobject("\\frac{d}{dx}(x^x)")
equation.set_height(creature.get_height() / 2)
equation.next_to(creature, RIGHT, aligned_edge=UP)
creature.look_at(equation)
return VGroup(creature, equation)
def get_aha_image(self):
creature = self.pi_creature.copy()
creature.change_mode("hooray")
from old_projects.eoc.chapter3 import NudgeSideLengthOfCube
scene = NudgeSideLengthOfCube(
end_at_animation_number=7,
skip_animations=True
)
group = VGroup(
scene.cube, scene.faces,
scene.bars, scene.corner_cube,
)
group.set_height(0.75 * creature.get_height())
group.next_to(creature, RIGHT)
creature.look_at(group)
return VGroup(creature, group)
def get_graphical_intuition_image(self):
gs = GraphScene()
gs.setup_axes()
graph = gs.get_graph(
lambda x: 0.2 * (x - 3) * (x - 5) * (x - 6) + 4,
x_min=2, x_max=8,
)
rects = gs.get_riemann_rectangles(
graph, x_min=2, x_max=8,
stroke_width=0.5,
dx=0.25
)
gs.add(graph, rects, gs.axes)
group = VGroup(*gs.mobjects)
self.adjust_size(group)
group.next_to(self.title, DOWN, MED_LARGE_BUFF)
group.rects = rects
group.continual_animations = [
turn_animation_into_updater(Write(rects)),
turn_animation_into_updater(ShowCreation(graph)),
turn_animation_into_updater(FadeIn(gs.axes)),
]
self.adjust_size(group)
return group
class GraphicalIntuitions(GraphScene):
CONFIG = {
"func": lambda x: 0.1 * (x - 2) * (x - 5) * (x - 7) + 4,
"x_labeled_nums": list(range(1, 10)),
}
def construct(self):
self.setup_axes()
axes = self.axes
graph = self.get_graph(self.func)
ss_group = self.get_secant_slope_group(
x=8, graph=graph, dx=0.01,
secant_line_length=6,
secant_line_color=RED,
)
rects = self.get_riemann_rectangles(
graph, x_min=2, x_max=8, dx=0.01, stroke_width=0
)
deriv_text = TextMobject(
"Derivative $\\rightarrow$ slope",
tex_to_color_map={"slope": ss_group.secant_line.get_color()}
)
deriv_text.to_edge(UP)
integral_text = TextMobject(
"Integral $\\rightarrow$ area",
tex_to_color_map={"area": rects[0].get_color()}
)
integral_text.next_to(deriv_text, DOWN)
self.play(
Succession(Write(axes), ShowCreation(graph, run_time=2)),
self.get_graph_words_anim(),
)
self.animate_secant_slope_group_change(
ss_group,
target_x=2,
rate_func=smooth,
run_time=2.5,
added_anims=[
Write(deriv_text),
VFadeIn(ss_group, run_time=2),
]
)
self.play(FadeIn(integral_text))
self.play(
LaggedStartMap(
GrowFromEdge, rects,
lambda r: (r, DOWN)
),
Animation(axes),
Animation(graph),
)
self.wait()
def get_graph_words_anim(self):
words = VGroup(
TextMobject("Graphs,"),
TextMobject("graphs,"),
TextMobject("non-stop graphs"),
TextMobject("all day"),
TextMobject("every day"),
TextMobject("as if to visualize is to graph"),
)
for word in words:
word.add_background_rectangle()
words.arrange(DOWN)
words.to_edge(UP)
return LaggedStartMap(
FadeIn, words,
rate_func=there_and_back,
run_time=len(words) - 1,
lag_ratio=0.6,
remover=True
)
class Wrapper(Scene):
CONFIG = {
"title": "",
"title_kwargs": {},
"screen_height": 6,
"wait_time": 2,
}
def construct(self):
rect = self.rect = ScreenRectangle(height=self.screen_height)
title = self.title = TextMobject(self.title, **self.title_kwargs)
title.to_edge(UP)
rect.next_to(title, DOWN)
self.add(title)
self.play(ShowCreation(rect))
self.wait(self.wait_time)
class DomainColoringWrapper(Wrapper):
CONFIG = {
"title": "Complex $\\rightarrow$ Complex",
}
class R2ToR2Wrapper(Wrapper):
CONFIG = {"title": "$\\mathds{R}^2 \\rightarrow \\mathds{R}^2$"}
class ExampleMultivariableFunction(LinearTransformationScene):
CONFIG = {
"show_basis_vectors": False,
"show_coordinates": True,
}
def construct(self):
def example_function(point):
x, y, z = point
return np.array([
x + np.sin(y),
y + np.sin(x),
0
])
self.wait()
self.apply_nonlinear_transformation(example_function, run_time=5)
self.wait()
class ChangingVectorFieldWrapper(Wrapper):
CONFIG = {"title": "$(x, y, t) \\rightarrow (x', y')$"}
class ChangingVectorField(Scene):
CONFIG = {
"wait_time": 30,
}
def construct(self):
plane = self.plane = NumberPlane()
plane.set_stroke(width=2)
plane.add_coordinates()
self.add(plane)
# Obviously a silly thing to do, but I'm sweeping
# through trying to make sure old scenes don't
# completely break in spots which used to have
# Continual animations
time_tracker = self.time_tracker = ValueTracker(0)
time_tracker.add_updater(
lambda t: t.set_value(self.get_time())
)
vectors = self.get_vectors()
vectors.add_updater(self.update_vectors)
self.add(vectors)
self.wait(self.wait_time)
def get_vectors(self):
vectors = VGroup()
x_max = int(np.ceil(FRAME_WIDTH))
y_max = int(np.ceil(FRAME_HEIGHT))
step = 0.5
for x in np.arange(-x_max, x_max + 1, step):
for y in np.arange(-y_max, y_max + 1, step):
point = x * RIGHT + y * UP
vectors.add(Vector(RIGHT).shift(point))
vectors.set_color_by_gradient(YELLOW, RED)
return vectors
def update_vectors(self, vectors):
time = self.time_tracker.get_value()
for vector in vectors:
point = vector.get_start()
out_point = self.func(point, time)
norm = get_norm(out_point)
if norm == 0:
out_point = RIGHT # Fake it
vector.set_fill(opacity=0)
else:
out_point *= 0.5
color = interpolate_color(BLUE, RED, norm / np.sqrt(8))
vector.set_fill(color, opacity=1)
vector.set_stroke(BLACK, width=1)
new_x, new_y = out_point[:2]
vector.put_start_and_end_on(
point, point + new_x * RIGHT + new_y * UP
)
def func(self, point, time):
x, y, z = point
return np.array([
np.sin(time + 0.5 * x + y),
np.cos(time + 0.2 * x * y + 0.7),
0
])
class MoreTopics(Scene):
def construct(self):
calculus = TextMobject("Calculus")
calculus.next_to(LEFT, LEFT)
calculus.set_color(YELLOW)
calculus.add_background_rectangle()
others = VGroup(
TextMobject("Multivariable calculus"),
TextMobject("Complex analysis"),
TextMobject("Differential geometry"),
TextMobject("$\\vdots$")
)
for word in others:
word.add_background_rectangle()
others.arrange(
DOWN, buff=MED_LARGE_BUFF, aligned_edge=LEFT,
)
others.next_to(RIGHT, RIGHT)
lines = VGroup(*[
Line(calculus.get_right(), word.get_left(), buff=MED_SMALL_BUFF)
for word in others
])
rect = FullScreenFadeRectangle(fill_opacity=0.7)
self.add(rect)
self.add(calculus)
self.play(
LaggedStartMap(ShowCreation, lines),
LaggedStartMap(Write, others),
)
self.wait()
self.calculus = calculus
self.lines = lines
self.full_screen_rect = rect
self.other_topics = others
class TransformationalViewWrapper(Wrapper):
CONFIG = {
"title": "Transformational view"
}
class SetTheStage(TeacherStudentsScene):
def construct(self):
ordinary = TextMobject("Ordinary visual")
transformational = TextMobject("Transformational visual")
for word in ordinary, transformational:
word.move_to(self.hold_up_spot, DOWN)
word.shift_onto_screen()
self.screen.scale(1.25, about_edge=UL)
self.add(self.screen)
self.teacher_holds_up(
ordinary,
added_anims=[self.get_student_changes(*3 * ["sassy"])]
)
self.wait()
self.play(
ordinary.shift, UP,
FadeInFromDown(transformational),
self.teacher.change, "hooray",
self.get_student_changes(*3 * ["erm"])
)
self.wait(3)
self.change_all_student_modes("pondering", look_at_arg=self.screen)
class StandardDerivativeVisual(GraphScene):
CONFIG = {
"y_max": 8,
"y_axis_height": 5,
}
def construct(self):
self.add_title()
self.show_function_graph()
self.show_slope_of_graph()
self.encourage_not_to_think_of_slope_as_definition()
self.show_sensitivity()
def add_title(self):
title = self.title = TextMobject("Standard derivative visual")
title.to_edge(UP)
h_line = Line(LEFT, RIGHT)
h_line.set_width(FRAME_WIDTH - 2 * LARGE_BUFF)
h_line.next_to(title, DOWN)
self.add(title, h_line)
def show_function_graph(self):
self.setup_axes()
def func(x):
x -= 5
return 0.1 * (x + 3) * (x - 3) * x + 3
graph = self.get_graph(func)
graph_label = self.get_graph_label(graph, x_val=9.5)
input_tracker = ValueTracker(4)
def get_x_value():
return input_tracker.get_value()
def get_y_value():
return graph.underlying_function(get_x_value())
def get_x_point():
return self.coords_to_point(get_x_value(), 0)
def get_y_point():
return self.coords_to_point(0, get_y_value())
def get_graph_point():
return self.coords_to_point(get_x_value(), get_y_value())
def get_v_line():
return DashedLine(get_x_point(), get_graph_point(), stroke_width=2)
def get_h_line():
return DashedLine(get_graph_point(), get_y_point(), stroke_width=2)
input_triangle = RegularPolygon(n=3, start_angle=TAU / 4)
output_triangle = RegularPolygon(n=3, start_angle=0)
for triangle in input_triangle, output_triangle:
triangle.set_fill(WHITE, 1)
triangle.set_stroke(width=0)
triangle.scale(0.1)
input_triangle_update = input_tracker.add_updater(
lambda m: m.move_to(get_x_point(), UP)
)
output_triangle_update = output_triangle.add_updater(
lambda m: m.move_to(get_y_point(), RIGHT)
)
x_label = TexMobject("x")
x_label_update = Mobject.add_updater(
x_label, lambda m: m.next_to(input_triangle, DOWN, SMALL_BUFF)
)
output_label = TexMobject("f(x)")
output_label_update = Mobject.add_updater(
output_label, lambda m: m.next_to(
output_triangle, LEFT, SMALL_BUFF)
)
v_line = get_v_line()
v_line_update = Mobject.add_updater(
v_line, lambda vl: Transform(vl, get_v_line()).update(1)
)
h_line = get_h_line()
h_line_update = Mobject.add_updater(
h_line, lambda hl: Transform(hl, get_h_line()).update(1)
)
graph_dot = Dot(color=YELLOW)
graph_dot_update = Mobject.add_updater(
graph_dot, lambda m: m.move_to(get_graph_point())
)
self.play(
ShowCreation(graph),
Write(graph_label),
)
self.play(
DrawBorderThenFill(input_triangle, run_time=1),
Write(x_label),
ShowCreation(v_line),
GrowFromCenter(graph_dot),
)
self.add_foreground_mobject(graph_dot)
self.play(
ShowCreation(h_line),
Write(output_label),
DrawBorderThenFill(output_triangle, run_time=1)
)
self.add(
input_triangle_update,
x_label_update,
graph_dot_update,
v_line_update,
h_line_update,
output_triangle_update,
output_label_update,
)
self.play(
input_tracker.set_value, 8,
run_time=6,
rate_func=there_and_back
)
self.input_tracker = input_tracker
self.graph = graph
def show_slope_of_graph(self):
input_tracker = self.input_tracker
deriv_input_tracker = ValueTracker(input_tracker.get_value())
# Slope line
def get_slope_line():
return self.get_secant_slope_group(
x=deriv_input_tracker.get_value(),
graph=self.graph,
dx=0.01,
secant_line_length=4
).secant_line
slope_line = get_slope_line()
slope_line_update = Mobject.add_updater(
slope_line, lambda sg: Transform(sg, get_slope_line()).update(1)
)
def position_deriv_label(deriv_label):
deriv_label.next_to(slope_line, UP)
return deriv_label
deriv_label = TexMobject(
"\\frac{df}{dx}(x) =", "\\text{Slope}", "="
)
deriv_label.get_part_by_tex("Slope").match_color(slope_line)
deriv_label_update = Mobject.add_updater(
deriv_label, position_deriv_label
)
slope_decimal = DecimalNumber(slope_line.get_slope())
slope_decimal.match_color(slope_line)
slope_decimal.add_updater(
lambda d: d.set_value(slope_line.get_slope())
)
slope_decimal.add_upater(
lambda d: d.next_to(
deriv_label, RIGHT, SMALL_BUFF
).shift(0.2 * SMALL_BUFF * DOWN)
)
self.play(
ShowCreation(slope_line),
Write(deriv_label),
Write(slope_decimal),
run_time=1
)
self.wait()
self.add(
slope_line_update,
# deriv_label_update,
)
for x in 9, 2, 4:
self.play(
input_tracker.set_value, x,
deriv_input_tracker.set_value, x,
run_time=3
)
self.wait()
self.deriv_input_tracker = deriv_input_tracker
def encourage_not_to_think_of_slope_as_definition(self):
morty = Mortimer(height=2)
morty.to_corner(DR)
self.play(FadeIn(morty))
self.play(PiCreatureSays(
morty, "Don't think of \\\\ this as the definition",
bubble_kwargs={"height": 2, "width": 4}
))
self.play(Blink(morty))
self.wait()
self.play(
RemovePiCreatureBubble(morty),
UpdateFromAlphaFunc(
morty, lambda m, a: m.set_fill(opacity=1 - a),
remover=True
)
)
def show_sensitivity(self):
input_tracker = self.input_tracker
deriv_input_tracker = self.deriv_input_tracker
self.wiggle_input()
for x in 9, 7, 2:
self.play(
input_tracker.set_value, x,
deriv_input_tracker.set_value, x,
run_time=3
)
self.wiggle_input()
###
def wiggle_input(self, dx=0.5, run_time=3):
input_tracker = self.input_tracker
x = input_tracker.get_value()
x_min = x - dx
x_max = x + dx
y, y_min, y_max = list(map(
self.graph.underlying_function,
[x, x_min, x_max]
))
x_line = Line(
self.coords_to_point(x_min, 0),
self.coords_to_point(x_max, 0),
)
y_line = Line(
self.coords_to_point(0, y_min),
self.coords_to_point(0, y_max),
)
x_rect, y_rect = rects = VGroup(Rectangle(), Rectangle())
rects.set_stroke(width=0)
rects.set_fill(YELLOW, 0.5)
x_rect.match_width(x_line)
x_rect.stretch_to_fit_height(0.25)
x_rect.move_to(x_line)
y_rect.match_height(y_line)
y_rect.stretch_to_fit_width(0.25)
y_rect.move_to(y_line)
self.play(
ApplyMethod(
input_tracker.set_value, input_tracker.get_value() + dx,
rate_func=lambda t: wiggle(t, 6)
),
FadeIn(
rects,
rate_func=squish_rate_func(smooth, 0, 0.33),
remover=True,
),
run_time=run_time,
)
self.play(FadeOut(rects))
class EoCWrapper(Scene):
def construct(self):
title = Title("Essence of calculus")
self.play(Write(title))
self.wait()
class IntroduceTransformationView(NumberlineTransformationScene):
CONFIG = {
"func": lambda x: 0.5 * np.sin(2 * x) + x,
"number_line_config": {
"x_min": 0,
"x_max": 6,
"unit_size": 2.0
},
}
def construct(self):
self.add_title()
self.show_animation_preview()
self.indicate_indicate_point_densities()
self.show_zoomed_transformation()
def add_title(self):
title = self.title = TextMobject("$f(x)$ as a transformation")
title.to_edge(UP)
self.add(title)
def show_animation_preview(self):
input_points = self.get_sample_input_points()
output_points = list(map(
self.number_func_to_point_func(self.func),
input_points
))
sample_dots = self.get_sample_dots()
sample_dot_ghosts = sample_dots.copy().fade(0.5)
arrows = VGroup(*[
Arrow(ip, op, buff=MED_SMALL_BUFF)
for ip, op in zip(input_points, output_points)
])
arrows = arrows[1::3]
arrows.set_stroke(BLACK, 1)
for sd in sample_dots:
sd.save_state()
sd.scale(2)
sd.fade(1)
self.play(LaggedStartMap(
ApplyMethod, sample_dots,
lambda sd: (sd.restore,),
run_time=2
))
self.play(LaggedStartMap(
GrowArrow, arrows,
run_time=6,
lag_ratio=0.3,
))
self.add(sample_dot_ghosts)
self.apply_function(
self.func, sample_dots=sample_dots,
run_time=3
)
self.wait()
self.play(LaggedStartMap(FadeOut, arrows, run_time=1))
self.sample_dots = sample_dots
self.sample_dot_ghosts = sample_dot_ghosts
def indicate_indicate_point_densities(self):
lower_brace = Brace(Line(LEFT, RIGHT), UP)
upper_brace = lower_brace.copy()
input_tracker = ValueTracker(0.5)
dx = 0.5
def update_upper_brace(brace):
x = input_tracker.get_value()
line = Line(
self.get_input_point(x),
self.get_input_point(x + dx),
)
brace.match_width(line, stretch=True)
brace.next_to(line, UP, buff=SMALL_BUFF)
return brace
def update_lower_brace(brace):
x = input_tracker.get_value()
line = Line(
self.get_output_point(self.func(x)),
self.get_output_point(self.func(x + dx)),
)
brace.match_width(line, stretch=True)
brace.next_to(line, UP, buff=SMALL_BUFF)
return brace
lower_brace_anim = UpdateFromFunc(lower_brace, update_lower_brace)
upper_brace_anim = UpdateFromFunc(upper_brace, update_upper_brace)
new_title = TextMobject(
"$\\frac{df}{dx}(x)$ measures stretch/squishing"
)
new_title.move_to(self.title, UP)
stretch_factor = DecimalNumber(0, color=YELLOW)
stretch_factor_anim = ChangingDecimal(
stretch_factor, lambda a: lower_brace.get_width() / upper_brace.get_width(),
position_update_func=lambda m: m.next_to(lower_brace, UP, SMALL_BUFF)
)
self.play(
GrowFromCenter(upper_brace),
FadeOut(self.title),
# FadeIn(new_title)
Write(new_title, run_time=2)
)
self.title = new_title
self.play(
ReplacementTransform(upper_brace.copy(), lower_brace),
GrowFromPoint(stretch_factor, upper_brace.get_center())
)
self.play(
input_tracker.set_value, self.input_line.x_max - dx,
lower_brace_anim,
upper_brace_anim,
stretch_factor_anim,
run_time=8,
rate_func=bezier([0, 0, 1, 1])
)
self.wait()
new_sample_dots = self.get_sample_dots()
self.play(
FadeOut(VGroup(
upper_brace, lower_brace, stretch_factor,
self.sample_dots, self.moving_input_line,
)),
FadeIn(new_sample_dots),
)
self.sample_dots = new_sample_dots
def show_zoomed_transformation(self):
x = 2.75
local_sample_dots = self.get_local_sample_dots(x)
self.zoom_in_on_input(
x,
local_sample_dots=local_sample_dots,
local_coordinate_values=self.get_local_coordinate_values(x),
)
self.wait()
self.apply_function(
self.func,
sample_dots=self.sample_dots,
local_sample_dots=local_sample_dots,
target_coordinate_values=self.get_local_coordinate_values(self.func(x))
)
self.wait()
class ExamplePlease(TeacherStudentsScene):
def construct(self):
self.student_says("Example?", student_index=0)
self.teacher_holds_up(TexMobject("f(x) = x^2").scale(1.5))
self.wait(2)
class TalkThroughXSquaredExample(IntroduceTransformationView):
CONFIG = {
"func": lambda x: x**2,
"number_line_config": {
"x_min": 0,
"x_max": 5,
"unit_size": 1.25,
},
"output_line_config": {
"x_max": 25,
},
"default_delta_x": 0.2
}
def construct(self):
self.add_title()
self.show_specific_points_mapping()
def add_title(self):
title = self.title = TextMobject("$f(x) = x^2$")
title.to_edge(UP, buff=MED_SMALL_BUFF)
self.add(title)
def show_specific_points_mapping(self):
# First, just show integers as examples
int_dots = self.get_sample_dots(1, 6, 1)
int_dot_ghosts = int_dots.copy().fade(0.5)
int_arrows = VGroup(*[
Arrow(
# num.get_bottom(),
self.get_input_point(x),
self.get_output_point(self.func(x)),
buff=MED_SMALL_BUFF
)
for x, num in zip(list(range(1, 6)), self.input_line.numbers[1:])
])
point_func = self.number_func_to_point_func(self.func)
numbers = self.input_line.numbers
numbers.next_to(self.input_line, UP, SMALL_BUFF)
self.titles[0].next_to(numbers, UP, MED_SMALL_BUFF, LEFT)
# map(TexMobject.add_background_rectangle, numbers)
# self.add_foreground_mobject(numbers)
for dot, dot_ghost, arrow in zip(int_dots, int_dot_ghosts, int_arrows):
arrow.match_color(dot)
self.play(DrawBorderThenFill(dot, run_time=1))
self.add(dot_ghost)
self.play(
GrowArrow(arrow),
dot.apply_function_to_position, point_func
)
self.wait()
# Show more sample_dots
sample_dots = self.get_sample_dots()
sample_dot_ghosts = sample_dots.copy().fade(0.5)
self.play(
LaggedStartMap(DrawBorderThenFill, sample_dots),
LaggedStartMap(FadeOut, int_arrows),
)
self.remove(int_dot_ghosts)
self.add(sample_dot_ghosts)
self.apply_function(self.func, sample_dots=sample_dots)
self.remove(int_dots)
self.wait()
self.sample_dots = sample_dots
self.sample_dot_ghosts = sample_dot_ghosts
def get_stretch_words(self, factor, color=RED, less_than_one=False):
factor_str = "$%s$" % str(factor)
result = TextMobject(
"Scale \\\\ by", factor_str,
tex_to_color_map={factor_str: color}
)
result.scale(0.7)
la, ra = TexMobject("\\leftarrow \\rightarrow")
if less_than_one:
la, ra = ra, la
if factor < 0:
kwargs = {
"path_arc": np.pi,
}
la = Arrow(UP, DOWN, **kwargs)
ra = Arrow(DOWN, UP, **kwargs)
for arrow in la, ra:
arrow.pointwise_become_partial(arrow, 0, 0.9)
arrow.tip.scale(2)
VGroup(la, ra).match_height(result)
la.next_to(result, LEFT)
ra.next_to(result, RIGHT)
result.add(la, ra)
result.next_to(
self.zoomed_display.get_top(), DOWN, SMALL_BUFF
)
return result
def get_deriv_equation(self, x, rhs, color=RED):
deriv_equation = self.deriv_equation = TexMobject(
"\\frac{df}{dx}(", str(x), ")", "=", str(rhs),
tex_to_color_map={str(x): color, str(rhs): color}
)
deriv_equation.next_to(self.title, DOWN, MED_LARGE_BUFF)
return deriv_equation
class ZoomInOnXSquaredNearOne(TalkThroughXSquaredExample):
def setup(self):
TalkThroughXSquaredExample.setup(self)
self.force_skipping()
self.add_title()
self.show_specific_points_mapping()
self.revert_to_original_skipping_status()
def construct(self):
zoom_words = TextMobject("Zoomed view \\\\ near 1")
zoom_words.next_to(self.zoomed_display, DOWN)
# zoom_words.shift_onto_screen()
x = 1
local_sample_dots = self.get_local_sample_dots(x)
local_coords = self.get_local_coordinate_values(x, dx=0.1)
zcbr_anim = self.zoomed_camera_background_rectangle_anim
zcbr_group = self.zoomed_camera_background_rectangle_group
frame = self.zoomed_camera.frame
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
self.zoom_in_on_input(x, local_sample_dots, local_coords)
self.play(FadeIn(zoom_words))
self.wait()
local_sample_dots.save_state()
frame.save_state()
self.mini_line.save_state()
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=local_sample_dots,
target_coordinate_values=local_coords
)
self.remove(sample_dot_ghost_copies)
self.wait()
# Go back
self.play(
frame.restore,
self.mini_line.restore,
local_sample_dots.restore,
zcbr_anim,
Animation(zcbr_group)
)
self.wait()
# Zoom in even more
extra_zoom_factor = 0.3
one_group = VGroup(
self.local_coordinates.tick_marks[1],
self.local_coordinates.numbers[1],
)
all_other_coordinates = VGroup(
self.local_coordinates.tick_marks[::2],
self.local_coordinates.numbers[::2],
self.local_target_coordinates,
)
self.play(frame.scale, extra_zoom_factor)
new_local_sample_dots = self.get_local_sample_dots(x, delta_x=0.005)
new_coordinate_values = self.get_local_coordinate_values(x, dx=0.02)
new_local_coordinates = self.get_local_coordinates(
self.input_line, *new_coordinate_values
)
self.play(
Write(new_local_coordinates),
Write(new_local_sample_dots),
one_group.scale, extra_zoom_factor, {"about_point": self.get_input_point(1)},
FadeOut(all_other_coordinates),
*[
ApplyMethod(dot.scale, extra_zoom_factor)
for dot in local_sample_dots
]
)
self.remove(one_group, local_sample_dots)
zcbr_group.remove(
self.local_coordinates, self.local_target_coordinates,
local_sample_dots
)
# Transform new zoomed view
stretch_by_two_words = self.get_stretch_words(2)
self.add_foreground_mobject(stretch_by_two_words)
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=new_local_sample_dots,
target_coordinate_values=new_coordinate_values,
added_anims=[FadeIn(stretch_by_two_words)]
)
self.remove(sample_dot_ghost_copies)
self.wait()
# Write derivative
deriv_equation = self.get_deriv_equation(1, 2, color=RED)
self.play(Write(deriv_equation))
self.wait()
class ZoomInOnXSquaredNearThree(ZoomInOnXSquaredNearOne):
CONFIG = {
"zoomed_display_width": 4,
}
def construct(self):
zoom_words = TextMobject("Zoomed view \\\\ near 3")
zoom_words.next_to(self.zoomed_display, DOWN)
x = 3
local_sample_dots = self.get_local_sample_dots(x)
local_coordinate_values = self.get_local_coordinate_values(x, dx=0.1)
target_coordinate_values = self.get_local_coordinate_values(self.func(x), dx=0.1)
color = self.sample_dots[len(self.sample_dots) / 2].get_color()
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
stretch_words = self.get_stretch_words(2 * x, color)
deriv_equation = self.get_deriv_equation(x, 2 * x, color)
self.add(deriv_equation)
self.zoom_in_on_input(
x,
pop_out=False,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values
)
self.play(Write(zoom_words, run_time=1))
self.wait()
self.add_foreground_mobject(stretch_words)
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=local_sample_dots,
target_coordinate_values=target_coordinate_values,
added_anims=[Write(stretch_words)]
)
self.wait(2)
class ZoomInOnXSquaredNearOneFourth(ZoomInOnXSquaredNearOne):
CONFIG = {
"zoom_factor": 0.01,
"local_coordinate_num_decimal_places": 4,
"zoomed_display_width": 4,
"default_delta_x": 0.25,
}
def construct(self):
# Much copy-pasting from previous scenes. Not great, but
# the fastest way to get the ease-of-tweaking I'd like.
zoom_words = TextMobject("Zoomed view \\\\ near $1/4$")
zoom_words.next_to(self.zoomed_display, DOWN)
x = 0.25
local_sample_dots = self.get_local_sample_dots(
x, sample_radius=2.5 * self.zoomed_camera.frame.get_width(),
)
local_coordinate_values = self.get_local_coordinate_values(
x, dx=0.01,
)
target_coordinate_values = self.get_local_coordinate_values(
self.func(x), dx=0.01,
)
color = RED
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
stretch_words = self.get_stretch_words("1/2", color, less_than_one=True)
deriv_equation = self.get_deriv_equation("1/4", "1/2", color)
one_fourth_point = self.get_input_point(x)
one_fourth_arrow = Vector(0.5 * UP, color=WHITE)
one_fourth_arrow.stem.stretch(0.75, 0)
one_fourth_arrow.tip.scale(0.75, about_edge=DOWN)
one_fourth_arrow.next_to(one_fourth_point, DOWN, SMALL_BUFF)
one_fourth_label = TexMobject("0.25")
one_fourth_label.match_height(self.input_line.numbers)
one_fourth_label.next_to(one_fourth_arrow, DOWN, SMALL_BUFF)
self.add(deriv_equation)
self.zoom_in_on_input(
x,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values,
pop_out=False,
first_added_anims=[
FadeIn(one_fourth_label),
GrowArrow(one_fourth_arrow),
]
)
self.play(Write(zoom_words, run_time=1))
self.wait()
self.add_foreground_mobject(stretch_words)
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=local_sample_dots,
target_coordinate_values=target_coordinate_values,
added_anims=[Write(stretch_words)]
)
self.wait(2)
class ZoomInOnXSquaredNearZero(ZoomInOnXSquaredNearOne):
CONFIG = {
"zoom_factor": 0.1,
"zoomed_display_width": 4,
"scale_by_term": "???",
}
def construct(self):
zoom_words = TextMobject(
"Zoomed %sx \\\\ near 0" % "{:,}".format(int(1.0 / self.zoom_factor))
)
zoom_words.next_to(self.zoomed_display, DOWN)
x = 0
local_sample_dots = self.get_local_sample_dots(
x, sample_radius=2 * self.zoomed_camera.frame.get_width()
)
local_coordinate_values = self.get_local_coordinate_values(
x, dx=self.zoom_factor
)
# target_coordinate_values = self.get_local_coordinate_values(
# self.func(x), dx=self.zoom_factor
# )
color = self.sample_dots[len(self.sample_dots) / 2].get_color()
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
stretch_words = self.get_stretch_words(
self.scale_by_term, color, less_than_one=True
)
deriv_equation = self.get_deriv_equation(x, 2 * x, color)
self.add(deriv_equation)
self.zoom_in_on_input(
x,
pop_out=False,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values
)
self.play(Write(zoom_words, run_time=1))
self.wait()
self.add_foreground_mobject(stretch_words)
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=local_sample_dots,
# target_coordinate_values=target_coordinate_values,
added_anims=[
Write(stretch_words),
MaintainPositionRelativeTo(
self.local_coordinates,
self.zoomed_camera.frame
)
]
)
self.wait(2)
class ZoomInMoreAndMoreToZero(ZoomInOnXSquaredNearZero):
def construct(self):
x = 0
color = self.sample_dots[len(self.sample_dots) / 2].get_color()
deriv_equation = self.get_deriv_equation(x, 2 * x, color)
self.add(deriv_equation)
frame = self.zoomed_camera.frame
zoomed_display_height = self.zoomed_display.get_height()
last_sample_dots = VGroup()
last_coords = VGroup()
last_zoom_words = None
for factor in 0.1, 0.01, 0.001, 0.0001:
frame.save_state()
frame.set_height(factor * zoomed_display_height)
self.local_coordinate_num_decimal_places = int(-np.log10(factor))
zoom_words = TextMobject(
"Zoomed", "{:,}x \\\\".format(int(1.0 / factor)),
"near 0",
)
zoom_words.next_to(self.zoomed_display, DOWN)
sample_dots = self.get_local_sample_dots(x)
coords = self.get_local_coordinate_values(x, dx=factor)
frame.restore()
added_anims = [
ApplyMethod(last_sample_dots.fade, 1),
ApplyMethod(last_coords.fade, 1),
]
if last_zoom_words is not None:
added_anims.append(ReplacementTransform(
last_zoom_words, zoom_words
))
else:
added_anims.append(FadeIn(zoom_words))
self.zoom_in_on_input(
x,
local_sample_dots=sample_dots,
local_coordinate_values=coords,
pop_out=False,
zoom_factor=factor,
first_added_anims=added_anims
)
self.wait()
last_sample_dots = sample_dots
last_coords = self.local_coordinates
last_zoom_words = zoom_words
class ZoomInOnXSquared100xZero(ZoomInOnXSquaredNearZero):
CONFIG = {
"zoom_factor": 0.01
}
class ZoomInOnXSquared1000xZero(ZoomInOnXSquaredNearZero):
CONFIG = {
"zoom_factor": 0.001,
"local_coordinate_num_decimal_places": 3,
}
class ZoomInOnXSquared10000xZero(ZoomInOnXSquaredNearZero):
CONFIG = {
"zoom_factor": 0.0001,
"local_coordinate_num_decimal_places": 4,
"scale_by_term": "0",
}
class XSquaredForNegativeInput(TalkThroughXSquaredExample):
CONFIG = {
"input_line_config": {
"x_min": -4,
"x_max": 4,
},
"input_line_zero_point": 0.5 * UP + 0 * LEFT,
"output_line_config": {},
"default_mapping_animation_config": {
"path_arc": 30 * DEGREES
},
"zoomed_display_width": 4,
}
def construct(self):
self.add_title()
self.show_full_transformation()
self.zoom_in_on_example()
def show_full_transformation(self):
sample_dots = self.get_sample_dots(
x_min=-4.005,
delta_x=0.05,
dot_radius=0.05
)
sample_dots.set_fill(opacity=0.8)
self.play(LaggedStartMap(DrawBorderThenFill, sample_dots))
self.play(LaggedStartMap(
ApplyFunction, sample_dots[len(sample_dots) / 2:0:-1],
lambda mob: (
lambda m: m.scale(2).shift(SMALL_BUFF * UP).set_color(PINK),
mob,
),
rate_func=there_and_back,
))
self.add_sample_dot_ghosts(sample_dots)
self.apply_function(self.func, sample_dots=sample_dots)
self.wait()
def zoom_in_on_example(self):
x = -2
local_sample_dots = self.get_local_sample_dots(x)
local_coordinate_values = self.get_local_coordinate_values(
x, dx=0.1
)
target_coordinate_values = self.get_local_coordinate_values(
self.func(x), dx=0.1
)
deriv_equation = self.get_deriv_equation(x, 2 * x, color=BLUE)
sample_dot_ghost_copies = self.sample_dot_ghosts.copy()
scale_words = self.get_stretch_words(-4, color=BLUE)
self.zoom_in_on_input(
x,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values,
)
self.wait()
self.play(Write(deriv_equation))
self.add_foreground_mobject(scale_words)
self.play(Write(scale_words))
self.apply_function(
self.func,
sample_dots=sample_dot_ghost_copies,
local_sample_dots=local_sample_dots,
target_coordinate_values=target_coordinate_values
)
self.wait()
class FeelsALittleCramped(TeacherStudentsScene):
def construct(self):
self.student_says(
"Kind of cramped,\\\\ isn't it?",
target_mode="sassy"
)
self.wait()
self.teacher_says(
"Sure, but think \\\\ locally"
)
self.change_all_student_modes("pondering", look_at_arg=self.screen)
self.wait(3)
class HowDoesThisSolveProblems(TeacherStudentsScene):
def construct(self):
self.student_says(
"Is this...useful?",
target_mode="confused"
)
self.change_student_modes("maybe", "confused", "sassy")
self.play(self.teacher.change, "happy")
self.wait(3)
class IntroduceContinuedFractionPuzzle(PiCreatureScene):
CONFIG = {
"remove_initial_rhs": True,
}
def construct(self):
self.ask_question()
self.set_equal_to_x()
def create_pi_creatures(self):
morty = Mortimer(height=2)
morty.to_corner(DR)
friend = PiCreature(color=GREEN, height=2)
friend.to_edge(DOWN)
friend.shift(LEFT)
group = VGroup(morty, friend)
group.shift(2 * LEFT)
return morty, friend
def ask_question(self):
morty, friend = self.pi_creatures
frac = get_phi_continued_fraction(9)
frac.scale(0.8)
rhs = DecimalNumber(
(1 - np.sqrt(5)) / 2.0,
num_decimal_places=5,
show_ellipsis=True,
)
rhs.set_color(YELLOW)
equals = TexMobject("=")
equals.next_to(frac.get_part_by_tex("\\over"), RIGHT)
rhs.next_to(equals, RIGHT)
group = VGroup(frac, equals, rhs)
group.scale(1.5)
group.to_corner(UR)
self.play(
LaggedStartMap(
Write, frac,
run_time=15,
lag_ratio=0.15,
),
FadeInFromDown(equals),
FadeInFromDown(rhs),
PiCreatureSays(
friend, "Would this be valid? \\\\ If not, why not?",
target_mode="confused",
look_at_arg=frac,
bubble_kwargs={
"direction": RIGHT,
"width": 4,
"height": 3,
}
),
morty.change, "pondering",
)
self.wait()
anims = [
RemovePiCreatureBubble(
friend, target_mode="pondering",
look_at_arg=frac
),
]
if self.remove_initial_rhs:
anims += [
Animation(frac),
FadeOut(equals),
rhs.scale, 0.5,
rhs.to_corner, DL,
]
self.play(*anims)
self.neg_one_over_phi = rhs
self.equals = equals
self.frac = frac
def set_equal_to_x(self):
frac = self.frac
morty, friend = self.get_pi_creatures()
inner_frac = frac[4:]
inner_frac_rect = SurroundingRectangle(
inner_frac, stroke_width=2, buff=0.5 * SMALL_BUFF
)
inner_frac_group = VGroup(inner_frac, inner_frac_rect)
equals = TexMobject("=")
equals.next_to(frac[3], RIGHT)
x, new_x = [TexMobject("x") for i in range(2)]
xs = VGroup(x, new_x)
xs.set_color(YELLOW)
xs.scale(1.3)
x.next_to(equals, RIGHT)
new_x.next_to(frac[3], DOWN, 2 * SMALL_BUFF)
fixed_point_words = VGroup(
TextMobject("Fixed point of"),
TexMobject(
"f(x) = 1 + \\frac{1}{x}",
tex_to_color_map={"x": YELLOW}
)
)
fixed_point_words.arrange(DOWN)
self.play(Write(x), Write(equals))
self.wait()
self.play(ShowCreation(inner_frac_rect))
self.wait()
self.play(
inner_frac_group.scale, 0.75,
inner_frac_group.center,
inner_frac_group.to_edge, LEFT,
ReplacementTransform(
x.copy(), new_x,
path_arc=-90 * DEGREES
)
)
self.wait()
self.play(
frac[3].stretch, 0.1, 0, {"about_edge": RIGHT},
MaintainPositionRelativeTo(
VGroup(frac[2], new_x), frac[3]
),
UpdateFromFunc(
frac[:2], lambda m: m.next_to(frac[3], LEFT)
)
)
self.wait()
fixed_point_words.next_to(VGroup(frac[0], xs), DOWN, LARGE_BUFF)
self.play(
Write(fixed_point_words),
morty.change, "hooray",
friend.change, "happy"
)
self.wait(3)
class GraphOnePlusOneOverX(GraphScene):
CONFIG = {
"x_min": -6,
"x_max": 6,
"x_axis_width": 12,
"y_min": -4,
"y_max": 5,
"y_axis_height": 8,
"y_axis_label": None,
"graph_origin": 0.5 * DOWN,
"num_graph_anchor_points": 100,
"func_graph_color": GREEN,
"identity_graph_color": BLUE,
}
def construct(self):
self.add_title()
self.setup_axes()
self.draw_graphs()
self.show_solutions()
def add_title(self):
title = self.title = TexMobject(
"\\text{Solve: }", "1 + \\frac{1}{x}", "=", "x",
)
title.set_color_by_tex("x", self.identity_graph_color, substring=False)
title.set_color_by_tex("frac", self.func_graph_color)
title.to_corner(UL)
self.add(title)
def setup_axes(self):
GraphScene.setup_axes(self)
step = 2
self.x_axis.add_numbers(*list(range(-6, 0, step)) + list(range(step, 7, step)))
self.y_axis.label_direction = RIGHT
self.y_axis.add_numbers(*list(range(-2, 0, step)) + list(range(step, 4, step)))
def draw_graphs(self, animate=True):
lower_func_graph, upper_func_graph = func_graph = VGroup(*[
self.get_graph(
lambda x: 1.0 + 1.0 / x,
x_min=x_min,
x_max=x_max,
color=self.func_graph_color,
)
for x_min, x_max in [(-10, -0.1), (0.1, 10)]
])
func_graph.label = self.get_graph_label(
upper_func_graph, "y = 1 + \\frac{1}{x}",
x_val=6, direction=UP,
)
identity_graph = self.get_graph(
lambda x: x, color=self.identity_graph_color
)
identity_graph.label = self.get_graph_label(
identity_graph, "y = x",
x_val=3, direction=UL, buff=SMALL_BUFF
)
if animate:
for graph in func_graph, identity_graph:
self.play(
ShowCreation(graph),
Write(graph.label),
run_time=2
)
self.wait()
else:
self.add(
func_graph, func_graph.label,
identity_graph, identity_graph.label,
)
self.func_graph = func_graph
self.identity_graph = identity_graph
def show_solutions(self):
phi = 0.5 * (1 + np.sqrt(5))
phi_bro = 0.5 * (1 - np.sqrt(5))
lines = VGroup()
for num in phi, phi_bro:
line = DashedLine(
self.coords_to_point(num, 0),
self.coords_to_point(num, num),
color=WHITE
)
line_copy = line.copy()
line_copy.set_color(YELLOW)
line.fade(0.5)
line_anim = ShowCreationThenDestruction(
line_copy,
lag_ratio=0.5,
run_time=2
)
cycle_animation(line_anim)
lines.add(line)
phi_line, phi_bro_line = lines
decimal_kwargs = {
"num_decimal_places": 3,
"show_ellipsis": True,
"color": YELLOW,
}
arrow_kwargs = {
"buff": SMALL_BUFF,
"color": WHITE,
"tip_length": 0.15,
"rectangular_stem_width": 0.025,
}
phi_decimal = DecimalNumber(phi, **decimal_kwargs)
phi_decimal.next_to(phi_line, DOWN, LARGE_BUFF)
phi_arrow = Arrow(
phi_decimal[:4].get_top(), phi_line.get_bottom(),
**arrow_kwargs
)
phi_label = TexMobject("=", "\\varphi")
phi_label.next_to(phi_decimal, RIGHT)
phi_label.set_color_by_tex("\\varphi", YELLOW)
phi_bro_decimal = DecimalNumber(phi_bro, **decimal_kwargs)
phi_bro_decimal.next_to(phi_bro_line, UP, LARGE_BUFF)
phi_bro_decimal.shift(0.5 * LEFT)
phi_bro_arrow = Arrow(
phi_bro_decimal[:6].get_bottom(), phi_bro_line.get_top(),
**arrow_kwargs
)
brother_words = TextMobject(
"$\\varphi$'s little brother",
tex_to_color_map={"$\\varphi$": YELLOW},
arg_separator=""
)
brother_words.next_to(
phi_bro_decimal[-2], UP, buff=MED_SMALL_BUFF,
aligned_edge=RIGHT
)
self.add(phi_line.continual_anim)
self.play(ShowCreation(phi_line))
self.play(
Write(phi_decimal),
GrowArrow(phi_arrow),
)
self.play(Write(phi_label))
self.wait(3)
self.add(phi_bro_line.continual_anim)
self.play(ShowCreation(phi_bro_line))
self.play(
Write(phi_bro_decimal),
GrowArrow(phi_bro_arrow),
)
self.wait(4)
self.play(Write(brother_words))
self.wait(8)
class ThinkAboutWithRepeatedApplication(IntroduceContinuedFractionPuzzle):
CONFIG = {
"remove_initial_rhs": False,
}
def construct(self):
self.force_skipping()
self.ask_question()
self.revert_to_original_skipping_status()
self.obviously_not()
self.ask_about_fraction()
self.plug_func_into_self()
def obviously_not(self):
morty, friend = self.get_pi_creatures()
friend.change_mode("confused")
randy = Randolph()
randy.match_height(morty)
randy.to_corner(DL)
frac = self.frac
rhs = self.neg_one_over_phi
plusses = frac[1::4]
plus_rects = VGroup(*[
SurroundingRectangle(plus, buff=0) for plus in plusses
])
plus_rects.set_color(PINK)
self.play(FadeIn(randy))
self.play(
PiCreatureSays(
randy, "Obviously not!",
bubble_kwargs={"width": 3, "height": 2},
target_mode="angry",
run_time=1,
),
morty.change, "guilty",
friend.change, "hesitant"
)
self.wait()
self.play(
Animation(frac),
RemovePiCreatureBubble(randy, target_mode="sassy"),
morty.change, "confused",
friend.change, "confused",
)
self.play(LaggedStartMap(
ShowCreationThenDestruction, plus_rects,
run_time=2,
lag_ratio=0.35,
))
self.play(WiggleOutThenIn(rhs))
self.wait(2)
self.play(
frac.scale, 0.7,
frac.to_corner, UL,
FadeOut(self.equals),
rhs.scale, 0.5,
rhs.center,
rhs.to_edge, LEFT,
FadeOut(randy),
morty.change, "pondering",
friend.change, "pondering",
)
def ask_about_fraction(self):
frac = self.frac
arrow = Vector(LEFT, color=RED)
arrow.next_to(frac, RIGHT)
question = TextMobject("What does this \\\\ actually mean?")
question.set_color(RED)
question.next_to(arrow, RIGHT)
self.play(
LaggedStartMap(FadeIn, question, run_time=1),
GrowArrow(arrow),
LaggedStartMap(
ApplyMethod, frac,
lambda m: (m.set_color, RED),
rate_func=there_and_back,
lag_ratio=0.2,
run_time=2
)
)
self.wait()
self.play(FadeOut(question), FadeOut(arrow))
def plug_func_into_self(self, value=1, value_str="1"):
morty, friend = self.pi_creatures
def func(x):
return 1 + 1.0 / x
lines = VGroup()
value_labels = VGroup()
for n_terms in range(5):
lhs = get_nested_f(n_terms, arg="c")
equals = TexMobject("=")
rhs = get_nested_one_plus_one_over_x(n_terms, bottom_term=value_str)
equals.next_to(rhs[0], LEFT)
lhs.next_to(equals, LEFT)
lines.add(VGroup(lhs, equals, rhs))
value_label = TexMobject("= %.3f\\dots" % value)
value = func(value)
value_labels.add(value_label)
lines.arrange(
DOWN, buff=MED_LARGE_BUFF,
)
VGroup(lines, value_labels).scale(0.8)
lines.to_corner(UR)
buff = MED_LARGE_BUFF + MED_SMALL_BUFF + value_labels.get_width()
lines.to_edge(RIGHT, buff=buff)
for line, value_label in zip(lines, value_labels):
value_label.move_to(line[1]).to_edge(RIGHT)
top_line = lines[0]
colors = [WHITE] + color_gradient([YELLOW, RED, PINK], len(lines) - 1)
for n in range(1, len(lines)):
color = colors[n]
lines[n][0].set_color(color)
lines[n][0][1:-1].match_style(lines[n - 1][0])
lines[n][2].set_color(color)
lines[n][2][4:].match_style(lines[n - 1][2])
arrow = Vector(0.5 * DOWN, color=WHITE)
arrow.next_to(value_labels[-1], DOWN)
q_marks = TexMobject("???")
q_marks.next_to(arrow, DOWN)
self.play(
FadeInFromDown(top_line),
FadeInFromDown(value_labels[0])
)
for n in range(1, len(lines)):
new_line = lines[n]
last_line = lines[n - 1]
value_label = value_labels[n]
mover, target = [
VGroup(
line[0][0],
line[0][-1],
line[1],
line[2][:4],
)
for line in (lines[1], new_line)
]
anims = [ReplacementTransform(
mover.copy().fade(1), target, path_arc=30 * DEGREES
)]
if n == 4:
morty.generate_target()
morty.target.change("horrified")
morty.target.shift(2.5 * DOWN)
anims.append(MoveToTarget(morty, remover=True))
self.play(*anims)
self.wait()
self.play(
ReplacementTransform(
last_line[0].copy(), new_line[0][1:-1]
),
ReplacementTransform(
last_line[2].copy(), new_line[2][4:]
),
)
self.play(FadeIn(value_label))
self.wait()
self.play(
GrowArrow(arrow),
Write(q_marks),
friend.change, "confused"
)
self.wait(3)
self.top_line = VGroup(lines[0], value_labels[0])
class RepeatedApplicationWithPhiBro(ThinkAboutWithRepeatedApplication):
CONFIG = {
"value": (1 - np.sqrt(5)) / 2,
"value_str": "-1/\\varphi",
}
def construct(self):
self.force_skipping()
self.ask_question()
self.obviously_not()
self.revert_to_original_skipping_status()
self.plug_func_into_self(
value=self.value,
value_str=self.value_str
)
class RepeatedApplicationWithNegativeSeed(RepeatedApplicationWithPhiBro, MovingCameraScene):
CONFIG = {
"value": -0.65,
"value_str": "-0.65"
}
def setup(self):
MovingCameraScene.setup(self)
RepeatedApplicationWithPhiBro.setup(self)
def construct(self):
RepeatedApplicationWithPhiBro.construct(self)
rect = SurroundingRectangle(self.top_line)
question = TextMobject("What about a negative seed?")
question.match_color(rect)
question.next_to(rect, UP)
self.play(ShowCreation(rect))
self.play(
Write(question),
self.camera.frame.set_height, FRAME_HEIGHT + 1.5
)
self.wait()
self.play(
FadeOut(question),
FadeOut(rect),
self.camera.frame.set_height, FRAME_HEIGHT
)
class ShowRepeatedApplication(Scene):
CONFIG = {
"title_color": YELLOW,
}
def construct(self):
self.add_func_title()
self.show_repeated_iteration()
def add_func_title(self):
title = self.title = VGroup(
TexMobject("f(", "x", ")"),
TexMobject("="),
get_nested_one_plus_one_over_x(1)
)
title.arrange(RIGHT)
title.to_corner(UL)
title.set_color(self.title_color)
self.add(title)
def show_repeated_iteration(self):
line = VGroup()
decimal_kwargs = {
"num_decimal_places": 3,
"show_ellipsis": True,
}
phi = (1 + np.sqrt(5)) / 2
def func(x):
return 1.0 + 1.0 / x
initial_term = DecimalNumber(2.71828, **decimal_kwargs)
line.add(initial_term)
last_term = initial_term
def get_arrow():
arrow = TexMobject("\\rightarrow")
arrow.stretch(1.5, 0)
arrow.next_to(line[-1], RIGHT)
tex = TexMobject("f(x)")
tex.set_color(YELLOW)
tex.match_width(arrow)
tex.next_to(arrow, UP, SMALL_BUFF)
return VGroup(arrow, tex)
for x in range(2):
arrow = get_arrow()
line.add(arrow)
new_term = DecimalNumber(
func(last_term.number),
**decimal_kwargs
)
new_term.next_to(arrow[0], RIGHT)
last_term = new_term
line.add(new_term)
line.add(get_arrow())
line.add(TexMobject("\\dots\\dots").next_to(line[-1][0], RIGHT))
num_phi_mob = DecimalNumber(phi, **decimal_kwargs)
line.add(num_phi_mob.next_to(line[-1], RIGHT))
line.move_to(DOWN)
rects = VGroup(*[
SurroundingRectangle(mob)
for mob in (line[0], line[1:-1], line[-1])
])
rects.set_stroke(BLUE, 2)
braces = VGroup(*[
Brace(rect, DOWN, buff=SMALL_BUFF)
for rect in rects
])
braces.set_color_by_gradient(GREEN, YELLOW)
brace_texts = VGroup(*[
brace.get_text(text).scale(0.75, about_edge=UP)
for brace, text in zip(braces, [
"Arbitrary \\\\ starting \\\\ value",
"Repeatedly apply $f(x)$",
"$\\varphi$ \\\\ ``Golden ratio''"
])
])
var_phi_mob = brace_texts[2][0]
var_phi_mob.scale(2, about_edge=UP).set_color(YELLOW)
brace_texts[2][1:].next_to(var_phi_mob, DOWN, MED_SMALL_BUFF)
# Animations
self.add(line[0])
self.play(
GrowFromCenter(braces[0]),
Write(brace_texts[0])
)
self.wait()
self.play(
GrowFromEdge(line[1], LEFT),
FadeIn(braces[1]),
FadeIn(brace_texts[1]),
)
self.play(ReplacementTransform(line[0].copy(), line[2]))
self.wait()
self.play(GrowFromEdge(line[3], LEFT))
self.play(ReplacementTransform(line[2].copy(), line[4]))
self.wait()
self.play(GrowFromEdge(line[5], LEFT))
self.play(LaggedStartMap(GrowFromCenter, line[6]))
self.wait()
self.play(FadeIn(line[7]))
self.play(
GrowFromCenter(braces[2]),
FadeIn(brace_texts[2]),
)
self.wait()
class NumericalPlayFromOne(ExternallyAnimatedScene):
pass
class NumericalPlayFromTau(ExternallyAnimatedScene):
pass
class NumericalPlayFromNegPhi(ExternallyAnimatedScene):
pass
class NumericalPlayOnNumberLineFromOne(Scene):
CONFIG = {
"starting_value": 1,
"n_jumps": 10,
"func": lambda x: 1 + 1.0 / x,
"number_line_config": {
"x_min": 0,
"x_max": 2,
"unit_size": 6,
"tick_frequency": 0.25,
"numbers_with_elongated_ticks": [0, 1, 2]
}
}
def construct(self):
self.add_number_line()
self.add_phi_label()
self.add_title()
self.bounce_around()
def add_number_line(self):
number_line = NumberLine(**self.number_line_config)
number_line.move_to(2 * DOWN)
number_line.add_numbers()
self.add(number_line)
self.number_line = number_line
def add_phi_label(self):
number_line = self.number_line
phi_point = number_line.number_to_point(
(1 + np.sqrt(5)) / 2
)
phi_dot = Dot(phi_point, color=YELLOW)
arrow = Vector(DL)
arrow.next_to(phi_point, UR, SMALL_BUFF)
phi_label = TexMobject("\\varphi = 1.618\\dots")
phi_label.set_color(YELLOW)
phi_label.next_to(arrow.get_start(), UP, SMALL_BUFF)
self.add(phi_dot, phi_label, arrow)
def add_title(self):
title = TexMobject("x \\rightarrow 1 + \\frac{1}{x}")
title.to_corner(UL)
self.add(title)
def bounce_around(self):
number_line = self.number_line
value = self.starting_value
point = number_line.number_to_point(value)
dot = Dot(point)
dot.set_fill(RED, opacity=0.8)
arrow = Vector(DR)
arrow.next_to(point, UL, buff=SMALL_BUFF)
arrow.match_color(dot)
start_here = TextMobject("Start here")
start_here.next_to(arrow.get_start(), UP, SMALL_BUFF)
start_here.match_color(dot)
self.play(
FadeIn(start_here),
GrowArrow(arrow),
GrowFromPoint(dot, arrow.get_start())
)
self.play(
FadeOut(start_here),
FadeOut(arrow)
)
self.wait()
for x in range(self.n_jumps):
new_value = self.func(value)
new_point = number_line.number_to_point(new_value)
if new_value - value > 0:
path_arc = -120 * DEGREES
else:
path_arc = -120 * DEGREES
arc = Line(
point, new_point,
path_arc=path_arc,
buff=SMALL_BUFF
)
self.play(
ShowCreationThenDestruction(arc, run_time=1.5),
ApplyMethod(
dot.move_to, new_point,
path_arc=path_arc
),
)
self.wait(0.5)
value = new_value
point = new_point
class NumericalPlayOnNumberLineFromTau(NumericalPlayOnNumberLineFromOne):
CONFIG = {
"starting_value": TAU,
"number_line_config": {
"x_min": 0,
"x_max": 7,
"unit_size": 2,
}
}
class NumericalPlayOnNumberLineFromMinusPhi(NumericalPlayOnNumberLineFromOne):
CONFIG = {
"starting_value": -0.61803,
"number_line_config": {
"x_min": -3,
"x_max": 3,
"unit_size": 2,
"tick_frequency": 0.25,
},
"n_jumps": 25,
}
def add_phi_label(self):
NumericalPlayOnNumberLineFromOne.add_phi_label(self)
number_line = self.number_line
new_point = number_line.number_to_point(
(1 - np.sqrt(5)) / 2
)
arrow = Vector(DR)
arrow.next_to(new_point, UL, SMALL_BUFF)
arrow.set_color(RED)
new_label = TexMobject("-\\frac{1}{\\varphi} = -0.618\\dots")
new_label.set_color(RED)
new_label.next_to(arrow.get_start(), UP, SMALL_BUFF)
new_label.shift(RIGHT)
self.add(new_label, arrow)
class RepeatedApplicationGraphically(GraphOnePlusOneOverX, PiCreatureScene):
CONFIG = {
"starting_value": 1,
"n_jumps": 5,
"n_times_to_show_identity_property": 2,
}
def setup(self):
GraphOnePlusOneOverX.setup(self)
PiCreatureScene.setup(self)
def construct(self):
self.setup_axes()
self.draw_graphs(animate=False)
self.draw_spider_web()
def create_pi_creature(self):
randy = Randolph(height=2)
randy.flip()
randy.to_corner(DR)
return randy
def get_new_randy_mode(self):
randy = self.pi_creature
if not hasattr(self, "n_mode_changes"):
self.n_mode_changes = 0
else:
self.n_mode_changes += 1
if self.n_mode_changes % 3 != 0:
return randy.get_mode()
return random.choice([
"confused",
"erm",
"maybe"
])
def draw_spider_web(self):
randy = self.pi_creature
func = self.func_graph[0].underlying_function
x_val = self.starting_value
curr_output = 0
dot = Dot(color=RED, fill_opacity=0.7)
dot.move_to(self.coords_to_point(x_val, curr_output))
self.play(FadeInFrom(dot, 2 * UR))
self.wait()
for n in range(self.n_jumps):
new_output = func(x_val)
func_graph_point = self.coords_to_point(x_val, new_output)
id_graph_point = self.coords_to_point(new_output, new_output)
v_line = DashedLine(dot.get_center(), func_graph_point)
h_line = DashedLine(func_graph_point, id_graph_point)
curr_output = new_output
x_val = new_output
for line in v_line, h_line:
line_end = line.get_end()
self.play(
ShowCreation(line),
dot.move_to, line_end,
randy.change, self.get_new_randy_mode()
)
self.wait()
if n < self.n_times_to_show_identity_property:
x_point = self.coords_to_point(new_output, 0)
y_point = self.coords_to_point(0, new_output)
lines = VGroup(*[
Line(dot.get_center(), point)
for point in (x_point, y_point)
])
lines.set_color(YELLOW)
self.play(ShowCreationThenDestruction(
lines, run_time=2
))
self.wait(0.25)
class RepeatedApplicationGraphicallyFromNegPhi(RepeatedApplicationGraphically):
CONFIG = {
"starting_value": -0.61,
"n_jumps": 13,
"n_times_to_show_identity_property": 0,
}
class LetsSwitchToTheTransformationalView(TeacherStudentsScene):
def construct(self):
self.teacher_says(
"Lose the \\\\ graphs!",
target_mode="hooray"
)
self.change_student_modes("hooray", "erm", "surprised")
self.wait(5)
class AnalyzeFunctionWithTransformations(NumberlineTransformationScene):
CONFIG = {
"input_line_zero_point": 0.5 * UP,
"output_line_zero_point": 2 * DOWN,
"func": lambda x: 1 + 1.0 / x,
"num_initial_applications": 10,
"num_repeated_local_applications": 7,
"zoomed_display_width": 3.5,
"zoomed_display_height": 2,
"default_mapping_animation_config": {},
}
def construct(self):
self.add_function_title()
self.repeatedly_apply_function()
self.show_phi_and_phi_bro()
self.zoom_in_on_phi()
self.zoom_in_on_phi_bro()
def setup_number_lines(self):
NumberlineTransformationScene.setup_number_lines(self)
for line in self.input_line, self.output_line:
VGroup(line, line.tick_marks).set_stroke(width=2)
def add_function_title(self):
title = TexMobject("f(x)", "=", "1 +", "\\frac{1}{x}")
title.to_edge(UP)
self.add(title)
self.title = title
def repeatedly_apply_function(self):
input_zero_point = self.input_line.number_to_point(0)
output_zero_point = self.output_line.number_to_point(0)
sample_dots = self.get_sample_dots(
delta_x=0.05,
dot_radius=0.05,
x_min=-10,
x_max=10,
)
sample_dots.set_stroke(BLACK, 0.5)
sample_points = list(map(Mobject.get_center, sample_dots))
self.play(LaggedStartMap(
FadeInFrom, sample_dots,
lambda m: (m, UP)
))
self.show_arrows(sample_points)
self.wait()
for x in range(self.num_initial_applications):
self.apply_function(
self.func,
apply_function_to_number_line=False,
sample_dots=sample_dots
)
self.wait()
shift_vect = input_zero_point - output_zero_point
shift_vect[0] = 0
lower_output_line = self.output_line.copy()
upper_output_line = self.output_line.copy()
lower_output_line.shift(-shift_vect)
lower_output_line.fade(1)
self.remove(self.output_line)
self.play(
ReplacementTransform(lower_output_line, self.output_line),
upper_output_line.shift, shift_vect,
upper_output_line.fade, 1,
sample_dots.shift, shift_vect,
)
self.remove(upper_output_line)
self.wait()
self.play(FadeOut(sample_dots))
def show_arrows(self, sample_points):
input_zero_point = self.input_line.number_to_point(0)
point_func = self.number_func_to_point_func(self.func)
alt_point_func = self.number_func_to_point_func(lambda x: 1.0 / x)
arrows, alt_arrows = [
VGroup(*[
Arrow(
point, func(point), buff=SMALL_BUFF,
tip_length=0.15
)
for point in sample_points
if get_norm(point - input_zero_point) > 0.3
])
for func in (point_func, alt_point_func)
]
for group in arrows, alt_arrows:
group.set_stroke(WHITE, 0.5)
group.set_color_by_gradient(RED, YELLOW)
for arrow in group:
arrow.tip.set_stroke(BLACK, 0.5)
one_plus = self.title.get_part_by_tex("1 +")
one_plus_rect = BackgroundRectangle(one_plus)
one_plus_rect.set_fill(BLACK, opacity=0.8)
self.play(LaggedStartMap(GrowArrow, arrows))
self.wait()
self.play(LaggedStartMap(
ApplyMethod, arrows,
lambda a: (a.scale, 0.7),
rate_func=there_and_back,
))
self.wait()
self.play(
Transform(arrows, alt_arrows),
FadeIn(one_plus_rect, remover=True),
rate_func=there_and_back_with_pause,
run_time=4,
)
self.wait()
self.all_arrows = arrows
def show_phi_and_phi_bro(self):
phi = (1 + np.sqrt(5)) / 2
phi_bro = (1 - np.sqrt(5)) / 2
input_phi_point = self.input_line.number_to_point(phi)
output_phi_point = self.output_line.number_to_point(phi)
input_phi_bro_point = self.input_line.number_to_point(phi_bro)
output_phi_bro_point = self.output_line.number_to_point(phi_bro)
tick = Line(UP, DOWN)
tick.set_stroke(YELLOW, 3)
tick.match_height(self.input_line.tick_marks)
phi_tick = tick.copy().move_to(input_phi_point, DOWN)
phi_bro_tick = tick.copy().move_to(input_phi_bro_point, DOWN)
VGroup(phi_tick, phi_bro_tick).shift(SMALL_BUFF * DOWN)
phi_label = TexMobject("1.618\\dots")
phi_label.next_to(phi_tick, UP)
phi_bro_label = TexMobject("-0.618\\dots")
phi_bro_label.next_to(phi_bro_tick, UP)
VGroup(phi_label, phi_bro_label).set_color(YELLOW)
arrow_kwargs = {
"buff": SMALL_BUFF,
"rectangular_stem_width": 0.035,
"tip_length": 0.2,
}
phi_arrow = Arrow(phi_tick, output_phi_point, **arrow_kwargs)
phi_bro_arrow = Arrow(phi_bro_tick, output_phi_bro_point, **arrow_kwargs)
def fade_arrow(arrow):
# arrow.set_stroke(DARK_GREY, 0.5)
arrow.set_stroke(width=0.1)
arrow.tip.set_fill(opacity=0)
arrow.tip.set_stroke(width=0)
return arrow
self.play(
LaggedStartMap(
ApplyFunction, self.all_arrows,
lambda a: (fade_arrow, a)
),
FadeIn(phi_arrow),
FadeIn(phi_bro_arrow),
)
self.play(
Write(phi_label),
GrowFromCenter(phi_tick)
)
self.play(
Write(phi_bro_label),
GrowFromCenter(phi_bro_tick)
)
self.set_variables_as_attrs(
input_phi_point, output_phi_point,
input_phi_bro_point, output_phi_bro_point,
phi_label, phi_tick,
phi_bro_label, phi_bro_tick,
phi_arrow, phi_bro_arrow
)
def zoom_in_on_phi(self):
phi = (1 + np.sqrt(5)) / 2
# phi_point = self.get_input_point(phi)
local_sample_dots = self.get_local_sample_dots(
phi, dot_radius=0.005, sample_radius=1
)
local_coordinate_values = [1.55, 1.6, 1.65, 1.7]
# zcbr = self.zoomed_camera_background_rectangle
zcbr_group = self.zoomed_camera_background_rectangle_group
zcbr_group.add(self.phi_tick)
title = self.title
deriv_text = TexMobject(
"|", "\\frac{df}{dx}(\\varphi)", "|", "< 1",
tex_to_color_map={"\\varphi": YELLOW}
)
deriv_text.get_parts_by_tex("|").match_height(
deriv_text, stretch=True
)
deriv_text.move_to(title, UP)
approx_value = TexMobject("\\approx |%.2f|" % (-1 / phi**2))
approx_value.move_to(deriv_text)
deriv_text_lhs = deriv_text[:-1]
deriv_text_rhs = deriv_text[-1]
self.zoom_in_on_input(
phi,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values
)
self.wait()
self.apply_function(
self.func,
apply_function_to_number_line=False,
local_sample_dots=local_sample_dots,
target_coordinate_values=local_coordinate_values,
)
self.wait()
self.play(
FadeInFromDown(deriv_text_lhs),
FadeInFromDown(deriv_text_rhs),
title.to_corner, UL
)
self.wait()
self.play(
deriv_text_lhs.next_to, approx_value, LEFT,
deriv_text_rhs.next_to, approx_value, RIGHT,
FadeIn(approx_value)
)
self.wait()
for n in range(self.num_repeated_local_applications):
self.apply_function(
self.func,
apply_function_to_number_line=False,
local_sample_dots=local_sample_dots,
path_arc=60 * DEGREES,
run_time=2
)
self.deriv_text = VGroup(
deriv_text_lhs, deriv_text_rhs, approx_value
)
def zoom_in_on_phi_bro(self):
zcbr = self.zoomed_camera_background_rectangle
# zcbr_group = self.zoomed_camera_background_rectangle_group
zoomed_frame = self.zoomed_camera.frame
phi_bro = (1 - np.sqrt(5)) / 2
# phi_bro_point = self.get_input_point(phi_bro)
local_sample_dots = self.get_local_sample_dots(phi_bro)
local_coordinate_values = [-0.65, -0.6, -0.55]
deriv_text = TexMobject(
"\\left| \\frac{df}{dx}\\left(\\frac{-1}{\\varphi}\\right) \\right|",
"\\approx |%.2f|" % (-1 / (phi_bro**2)),
"> 1"
)
deriv_text.move_to(self.deriv_text, UL)
deriv_text[0][10:14].set_color(YELLOW)
self.play(
zoomed_frame.set_height, 4,
zoomed_frame.center,
self.deriv_text.fade, 1,
run_time=2
)
self.wait()
zcbr.set_fill(opacity=0)
self.zoom_in_on_input(
phi_bro,
local_sample_dots=local_sample_dots,
local_coordinate_values=local_coordinate_values,
zoom_factor=self.zoom_factor,
first_anim_kwargs={"run_time": 2},
)
self.wait()
self.play(FadeInFromDown(deriv_text))
self.wait()
zcbr.set_fill(opacity=1)
self.apply_function(
self.func,
apply_function_to_number_line=False,
local_sample_dots=local_sample_dots,
target_coordinate_values=local_coordinate_values,
)
self.wait()
for n in range(self.num_repeated_local_applications):
self.apply_function(
self.func,
apply_function_to_number_line=False,
local_sample_dots=local_sample_dots,
path_arc=20 * DEGREES,
run_time=2,
)
class StabilityAndInstability(AnalyzeFunctionWithTransformations):
CONFIG = {
"num_initial_applications": 0,
}
def construct(self):
self.force_skipping()
self.add_function_title()
self.repeatedly_apply_function()
self.show_phi_and_phi_bro()
self.revert_to_original_skipping_status()
self.label_stability()
self.write_derivative_fact()
def label_stability(self):
self.title.to_corner(UL)
stable_label = TextMobject("Stable fixed point")
unstable_label = TextMobject("Unstable fixed point")
labels = VGroup(stable_label, unstable_label)
labels.scale(0.8)
stable_label.next_to(self.phi_label, UP, aligned_edge=ORIGIN)
unstable_label.next_to(self.phi_bro_label, UP, aligned_edge=ORIGIN)
phi_point = self.input_phi_point
phi_bro_point = self.input_phi_bro_point
arrow_groups = VGroup()
for point in phi_point, phi_bro_point:
arrows = VGroup(*[a for a in self.all_arrows if get_norm(a.get_start() - point) < 0.75]).copy()
arrows.set_fill(PINK, 1)
arrows.set_stroke(PINK, 3)
arrows.second_anim = LaggedStartMap(
ApplyMethod, arrows,
lambda m: (m.set_color, YELLOW),
rate_func=there_and_back_with_pause,
lag_ratio=0.7,
run_time=2,
)
arrows.anim = AnimationGroup(*list(map(GrowArrow, arrows)))
arrow_groups.add(arrows)
phi_arrows, phi_bro_arrows = arrow_groups
self.add_foreground_mobjects(self.phi_arrow, self.phi_bro_arrow)
self.play(
Write(stable_label),
phi_arrows.anim,
)
self.play(phi_arrows.second_anim)
self.play(
Write(unstable_label),
phi_bro_arrows.anim,
)
self.play(phi_bro_arrows.second_anim)
self.wait()
self.stable_label = stable_label
self.unstable_label = unstable_label
self.phi_arrows = phi_arrows
self.phi_bro_arrows = phi_bro_arrows
def write_derivative_fact(self):
stable_label = self.stable_label
unstable_label = self.unstable_label
labels = VGroup(stable_label, unstable_label)
phi_arrows = self.phi_arrows
phi_bro_arrows = self.phi_bro_arrows
arrow_groups = VGroup(phi_arrows, phi_bro_arrows)
deriv_labels = VGroup()
for char, label in zip("<>", labels):
deriv_label = TexMobject(
"\\big|", "\\frac{df}{dx}(", "x", ")", "\\big|",
char, "1"
)
deriv_label.get_parts_by_tex("\\big|").match_height(
deriv_label, stretch=True
)
deriv_label.set_color_by_tex("x", YELLOW, substring=False)
deriv_label.next_to(label, UP)
deriv_labels.add(deriv_label)
dot_groups = VGroup()
for arrow_group in arrow_groups:
dot_group = VGroup()
for arrow in arrow_group:
start_point, end_point = [
line.number_to_point(line.point_to_number(p))
for line, p in [
(self.input_line, arrow.get_start()),
(self.output_line, arrow.get_end()),
]
]
dot = Dot(start_point, radius=0.05)
dot.set_color(YELLOW)
dot.generate_target()
dot.target.move_to(end_point)
dot_group.add(dot)
dot_groups.add(dot_group)
for deriv_label, dot_group in zip(deriv_labels, dot_groups):
self.play(FadeInFromDown(deriv_label))
self.play(LaggedStartMap(GrowFromCenter, dot_group))
self.play(*list(map(MoveToTarget, dot_group)), run_time=2)
self.wait()
class StaticAlgebraicObject(Scene):
def construct(self):
frac = get_phi_continued_fraction(40)
frac.set_width(FRAME_WIDTH - 1)
# frac.shift(2 * DOWN)
frac.to_edge(DOWN)
frac.set_stroke(WHITE, width=0.5)
title = TexMobject(
"\\infty \\ne \\lim",
tex_to_color_map={"\\ne": RED}
)
title.scale(1.5)
title.to_edge(UP)
polynomial = TexMobject("x^2 - x - 1 = 0")
polynomial.move_to(title)
self.add(title)
self.play(LaggedStartMap(
GrowFromCenter, frac,
lag_ratio=0.1,
run_time=3
))
self.wait()
factor = 1.1
self.play(frac.scale, factor, run_time=0.5)
self.play(
frac.scale, 1 / factor,
frac.set_color, LIGHT_GREY,
run_time=0.5, rate_func=lambda t: t**5,
)
self.wait()
self.play(
FadeOut(title),
FadeIn(polynomial)
)
self.wait(2)
class NotBetterThanGraphs(TeacherStudentsScene):
def construct(self):
self.student_says(
"Um, yeah, I'll stick \\\\ with graphs thanks",
target_mode="sassy",
)
self.play(
self.teacher.change, "guilty",
self.get_student_changes("sad", "sassy", "hesitant")
)
self.wait(2)
self.play(
RemovePiCreatureBubble(self.students[1]),
self.teacher.change, "raise_right_hand"
)
self.change_all_student_modes(
"confused", look_at_arg=self.screen
)
self.wait(3)
self.teacher_says(
"You must flex those \\\\ conceptual muscles",
added_anims=[self.get_student_changes(
*3 * ["thinking"],
look_at_arg=self.teacher.eyes
)]
)
self.wait(3)
class WhatComesAfterWrapper(Wrapper):
CONFIG = {"title": "Beyond the first year"}
def construct(self):
Wrapper.construct(self)
new_title = TextMobject("Next video")
new_title.set_color(BLUE)
new_title.move_to(self.title)
self.play(
FadeInFromDown(new_title),
self.title.shift, UP,
self.title.fade, 1,
)
self.wait(3)
class TopicsAfterSingleVariable(PiCreatureScene, MoreTopics):
CONFIG = {
"pi_creatures_start_on_screen": False,
}
def construct(self):
MoreTopics.construct(self)
self.show_horror()
self.zero_in_on_complex_analysis()
def create_pi_creatures(self):
creatures = VGroup(*[
PiCreature(color=color)
for color in [BLUE_E, BLUE_C, BLUE_D]
])
creatures.arrange(RIGHT, buff=LARGE_BUFF)
creatures.scale(0.5)
creatures.to_corner(DR)
return creatures
def show_horror(self):
creatures = self.get_pi_creatures()
modes = ["horrified", "tired", "horrified"]
for creature, mode in zip(creatures, modes):
creature.generate_target()
creature.target.change(mode, self.other_topics)
creatures.fade(1)
self.play(LaggedStartMap(MoveToTarget, creatures))
self.wait(2)
def zero_in_on_complex_analysis(self):
creatures = self.get_pi_creatures()
complex_analysis = self.other_topics[1]
self.other_topics.remove(complex_analysis)
self.play(
complex_analysis.scale, 1.25,
complex_analysis.center,
complex_analysis.to_edge, UP,
LaggedStartMap(FadeOut, self.other_topics),
LaggedStartMap(FadeOut, self.lines),
FadeOut(self.calculus),
*[
ApplyMethod(creature.change, "pondering")
for creature in creatures
]
)
self.wait(4)
class ShowJacobianZoomedIn(LinearTransformationScene, ZoomedScene):
CONFIG = {
"show_basis_vectors": False,
"show_coordinates": True,
"zoom_factor": 0.05,
}
def setup(self):
LinearTransformationScene.setup(self)
ZoomedScene.setup(self)
def construct(self):
def example_function(point):
x, y, z = point
return np.array([
x + np.sin(y),
y + np.sin(x),
0
])
zoomed_camera = self.zoomed_camera
zoomed_display = self.zoomed_display
frame = zoomed_camera.frame
frame.move_to(3 * LEFT + 1 * UP)
frame.set_color(YELLOW)
zoomed_display.display_frame.set_color(YELLOW)
zd_rect = BackgroundRectangle(
zoomed_display,
fill_opacity=1,
buff=MED_SMALL_BUFF,
)
self.add_foreground_mobject(zd_rect)
zd_rect.anim = UpdateFromFunc(
zd_rect,
lambda rect: rect.replace(zoomed_display).scale(1.1)
)
zd_rect.next_to(FRAME_HEIGHT * UP, UP)
tiny_grid = NumberPlane(
x_radius=2,
y_radius=2,
color=BLUE_E,
secondary_color=DARK_GREY,
)
tiny_grid.replace(frame)
jacobian_words = TextMobject("Jacobian")
jacobian_words.add_background_rectangle()
jacobian_words.scale(1.5)
jacobian_words.move_to(zoomed_display, UP)
zoomed_display.next_to(jacobian_words, DOWN)
self.play(self.get_zoom_in_animation())
self.activate_zooming()
self.play(
self.get_zoomed_display_pop_out_animation(),
zd_rect.anim
)
self.play(
ShowCreation(tiny_grid),
Write(jacobian_words),
run_time=2
)
self.add_transformable_mobject(tiny_grid)
self.add_foreground_mobject(jacobian_words)
self.wait()
self.apply_nonlinear_transformation(
example_function,
added_anims=[MaintainPositionRelativeTo(
zoomed_camera.frame, tiny_grid,
)],
run_time=5
)
self.wait()
class PrinciplesOverlay(PiCreatureScene):
CONFIG = {
"default_pi_creature_kwargs": {
"color": GREY_BROWN,
"flip_at_start": True,
},
"default_pi_creature_start_corner": DR,
}
def construct(self):
morty = self.pi_creature
q_marks = VGroup(*[TexMobject("?") for x in range(40)])
q_marks.arrange_in_grid(4, 10)
q_marks.space_out_submobjects(1.4)
for mark in q_marks:
mark.shift(
random.random() * RIGHT,
random.random() * UP,
)
mark.scale(1.5)
mark.set_stroke(BLACK, 1)
q_marks.next_to(morty, UP)
q_marks.shift_onto_screen()
q_marks.sort(
lambda p: get_norm(p - morty.get_top())
)
self.play(morty.change, "pondering")
self.wait(2)
self.play(morty.change, "raise_right_hand")
self.wait()
self.play(morty.change, "thinking")
self.wait(4)
self.play(FadeInFromDown(q_marks[0]))
self.wait(2)
self.play(LaggedStartMap(
FadeInFromDown, q_marks[1:],
run_time=3
))
self.wait(3)
class ManyInfiniteExpressions(Scene):
def construct(self):
frac = get_phi_continued_fraction(10)
frac.set_height(2)
frac.to_corner(UL)
n = 9
radical_str_parts = [
"%d + \\sqrt{" % d
for d in range(1, n + 1)
]
radical_str_parts += ["\\cdots"]
radical_str_parts += ["}"] * n
radical = TexMobject("".join(radical_str_parts))
radical.to_corner(UR)
radical.to_edge(DOWN)
radical.set_color_by_gradient(YELLOW, RED)
n = 12
power_tower = TexMobject(
*["\\sqrt{2}^{"] * n + ["\\dots"] + ["}"] * n
)
power_tower.to_corner(UR)
power_tower.set_color_by_gradient(BLUE, GREEN)
self.play(*[
LaggedStartMap(
GrowFromCenter, group,
lag_ratio=0.1,
run_time=8,
)
for group in (frac, radical, power_tower)
])
self.wait(2)
class HoldUpPromo(PrinciplesOverlay):
def construct(self):
morty = self.pi_creature
url = TextMobject("https://brilliant.org/3b1b/")
url.to_corner(UL)
rect = ScreenRectangle(height=5.5)
rect.next_to(url, DOWN)
rect.to_edge(LEFT)
self.play(
Write(url),
self.pi_creature.change, "raise_right_hand"
)
self.play(ShowCreation(rect))
self.wait(2)
self.change_mode("thinking")
self.wait()
self.look_at(url)
self.wait(10)
self.change_mode("happy")
self.wait(10)
self.change_mode("raise_right_hand")
self.wait(10)
self.play(FadeOut(rect), FadeOut(url))
self.play(morty.change, "raise_right_hand")
self.wait()
self.play(morty.change, "hooray")
self.wait(3)
class EndScreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"Juan Benet",
"Keith Smith",
"Chloe Zhou",
"Desmos",
"Burt Humburg",
"CrypticSwarm",
"Andrew Sachs",
"Ho\\`ang T\\`ung L\\^am",
# "Hoàng Tùng Lâm",
"Devin Scott",
"Akash Kumar",
"Felix Tripier",
"Arthur Zey",
"David Kedmey",
"Ali Yahya",
"Mayank M. Mehrotra",
"Lukas Biewald",
"Yana Chernobilsky",
"Kaustuv DeBiswas",
"Yu Jun",
"dave nicponski",
"Damion Kistler",
"Jordan Scales",
"Markus Persson",
"Fela",
"Fred Ehrsam",
"Britt Selvitelle",
"Jonathan Wilson",
"Ryan Atallah",
"Joseph John Cox",
"Luc Ritchie",
"Matt Roveto",
"Jamie Warner",
"Marek Cirkos",
"Magister Mugit",
"Stevie Metke",
"Cooper Jones",
"James Hughes",
"John V Wertheim",
"Chris Giddings",
"Song Gao",
"Alexander Feldman",
"Matt Langford",
"Max Mitchell",
"Richard Burgmann",
"John Griffith",
"Chris Connett",
"Steven Tomlinson",
"Jameel Syed",
"Bong Choung",
"Ignacio Freiberg",
"Zhilong Yang",
"Giovanni Filippi",
"Eric Younge",
"Prasant Jagannath",
"Cody Brocious",
"James H. Park",
"Norton Wang",
"Kevin Le",
"Tianyu Ge",
"David MacCumber",
"Oliver Steele",
"Yaw Etse",
"Dave B",
"Waleed Hamied",
"George Chiesa",
"supershabam",
"Delton Ding",
"Thomas Tarler",
"Isak Hietala",
"1st ViewMaths",
"Jacob Magnuson",
"Mark Govea",
"Clark Gaebel",
"Mathias Jansson",
"David Clark",
"Michael Gardner",
"Mads Elvheim",
"Awoo",
"Dr . David G. Stork",
"Ted Suzman",
"Linh Tran",
"Andrew Busey",
"John Haley",
"Ankalagon",
"Eric Lavault",
"Boris Veselinovich",
"Julian Pulgarin",
"Jeff Linse",
"Robert Teed",
"Jason Hise",
"Meshal Alshammari",
"Bernd Sing",
"James Thornton",
"Mustafa Mahdi",
"Mathew Bramson",
"Jerry Ling",
"Sh\\`im\\'in Ku\\=ang",
"Rish Kundalia",
"Achille Brighton",
"Ripta Pasay",
]
}
# class Thumbnail(GraphicalIntuitions):
class Thumbnail(AnalyzeFunctionWithTransformations):
CONFIG = {
"x_axis_width": 12,
"graph_origin": 1.5 * DOWN + 4 * LEFT,
"num_initial_applications": 1,
"input_line_zero_point": 2 * UP,
}
def construct(self):
self.add_function_title()
self.title.fade(1)
self.titles.fade(1)
self.repeatedly_apply_function()
self.all_arrows.set_stroke(width=1)
full_rect = FullScreenFadeRectangle()
cross = Cross(full_rect)
cross.set_stroke(width=40)
# self.add(cross)