Files
manim/active_projects/alt_calc.py
2018-05-09 15:06:01 -07:00

468 lines
15 KiB
Python

from big_ol_pile_of_manim_imports import *
class NumberlineTransformationScene(MovingCameraScene):
CONFIG = {
"input_line_zero_point": 1 * 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_anchors": 20,
"default_delta_x": 0.1,
"default_sample_dot_radius": 0.05,
"default_sample_dot_colors": [RED, YELLOW],
"default_mapping_animation_config": {
"run_time": 3,
# "path_arc": 30 * DEGREES,
}
}
def setup(self):
MovingCameraScene.setup(self)
self.setup_number_lines()
self.setup_titles()
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.main_line.insert_n_anchor_points(
self.num_inserted_number_line_anchors
)
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()
for word in "Inputs", "Outputs"
])
for title, line in zip(self.titles, self.number_lines):
title.next_to(line, UP)
title.shift_onto_screen()
self.add(self.titles)
def get_line_mapping_animation(self, func, **kwargs):
anim_config = dict(self.default_mapping_animation_config)
anim_config.update(kwargs)
input_line_copy = self.input_line.deepcopy()
input_line_copy.remove(input_line_copy.numbers)
# input_line_copy.set_stroke(width=2)
input_line_copy.generate_target(use_deepcopy=True)
point_func = self.number_func_to_point_func(func)
input_line_copy.target.main_line.apply_function(point_func)
for tick in input_line_copy.target.tick_marks:
tick.move_to(point_func(tick.get_center()))
return MoveToTarget(input_line_copy, **anim_config)
def get_sample_dots(self, x_min=None, x_max=None,
delta_x=None, radius=None, colors=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
radius = radius or self.default_sample_dot_radius
colors = colors or self.default_sample_dot_colors
dots = self.sample_dots = VGroup(*[
Dot(self.input_line.number_to_point(x), radius=radius)
for x in np.arange(x_min, x_max+delta_x, delta_x)
])
dots.set_color_by_gradient(*colors)
return dots
def get_sample_dots_mapping_animation(self, func, **kwargs):
anim_config = dict(self.default_mapping_animation_config)
anim_config.update(kwargs)
point_func = self.number_func_to_point_func(func)
return AnimationGroup(*[
ApplyPointwiseFunctionToCenter(point_func, dot, **anim_config)
for dot in self.sample_dots
])
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
line_anim = self.get_line_mapping_animation(func)
sample_dots = self.get_sample_dots()
sample_dots_anim = self.get_sample_dots_mapping_animation(func)
self.add(sample_dots)
self.play(line_anim, sample_dots_anim)
self.wait()
self.play(
self.camera_frame.scale, 2, {"about_edge": LEFT},
run_time=3,
rate_func=there_and_back_with_pause,
)
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.scale_to_fit_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(LaggedStart(
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 = {
}
def construct(self):
randy = self.pi_creature
deriv_equation = TexMobject(
"\\frac{df}{dx}(x) = \\lim_{\\Delta x \\to \\infty}" +
"{f(x + \\Delta x) - f(x) \\over \\Delta x}",
tex_to_color_map={"\\Delta x": BLUE}
)
title = TextMobject("Calculus 101")
title.to_edge(UP)
h_line = Line(LEFT, RIGHT)
h_line.scale_to_fit_width(FRAME_WIDTH - LARGE_BUFF)
h_line.next_to(title, DOWN)
self.add(title, h_line)
self.play(randy.change, "erm", title)
self.wait()
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.scale_to_fit_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 = ContinualUpdateFromFunc(
input_triangle, lambda m: m.move_to(get_x_point(), UP)
)
output_triangle_update = ContinualUpdateFromFunc(
output_triangle, lambda m: m.move_to(get_y_point(), RIGHT)
)
x_label = TexMobject("x")
x_label_update = ContinualUpdateFromFunc(
x_label, lambda m: m.next_to(input_triangle, DOWN, SMALL_BUFF)
)
output_label = TexMobject("f(x)")
output_label_update = ContinualUpdateFromFunc(
output_label, lambda m: m.next_to(
output_triangle, LEFT, SMALL_BUFF)
)
v_line = get_v_line()
v_line_update = ContinualUpdateFromFunc(
v_line, lambda vl: Transform(vl, get_v_line()).update(1)
)
h_line = get_h_line()
h_line_update = ContinualUpdateFromFunc(
h_line, lambda hl: Transform(hl, get_h_line()).update(1)
)
graph_dot = Dot(color=YELLOW)
graph_dot_update = ContinualUpdateFromFunc(
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 = ContinualUpdateFromFunc(
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 = ContinualUpdateFromFunc(
deriv_label, position_deriv_label
)
slope_decimal = DecimalNumber(slope_line.get_slope())
slope_decimal.match_color(slope_line)
slope_decimal_update = ContinualChangingDecimal(
slope_decimal, lambda dt: slope_line.get_slope(),
position_update_func=lambda m: m.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,
slope_decimal_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 = 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()