Beginning related rates example in eoc5

This commit is contained in:
Grant Sanderson
2017-03-09 15:50:40 -08:00
parent 5b08973b37
commit 311a22048b
4 changed files with 403 additions and 33 deletions

View File

@ -80,20 +80,15 @@ class SlopeOfCircleExample(ZoomedScene):
"zoomed_canvas_corner_buff" : MED_SMALL_BUFF, "zoomed_canvas_corner_buff" : MED_SMALL_BUFF,
} }
def construct(self): def construct(self):
should_skip_animations = self.skip_animations
self.skip_animations = True
self.setup_plane() self.setup_plane()
self.introduce_circle() self.introduce_circle()
self.talk_through_pythagorean_theorem() self.talk_through_pythagorean_theorem()
self.draw_example_slope() self.draw_example_slope()
# self.show_perpendicular_radius() self.show_perpendicular_radius()
self.show_dx_and_dy() self.show_dx_and_dy()
self.write_slope_as_dy_dx() self.write_slope_as_dy_dx()
# self.point_out_this_is_not_a_graph() self.point_out_this_is_not_a_graph()
self.skip_animations = should_skip_animations
self.perform_implicit_derivative() self.perform_implicit_derivative()
self.show_rearrangement()
self.show_final_slope() self.show_final_slope()
def setup_plane(self): def setup_plane(self):
@ -115,6 +110,7 @@ class SlopeOfCircleExample(ZoomedScene):
circle.point_from_proportion(1./8), circle.point_from_proportion(1./8),
UP+RIGHT UP+RIGHT
) )
equation.to_edge(RIGHT)
self.play(ShowCreation(circle, run_time = 2)) self.play(ShowCreation(circle, run_time = 2))
self.play(Write(equation)) self.play(Write(equation))
@ -239,12 +235,11 @@ class SlopeOfCircleExample(ZoomedScene):
self.play(PiCreatureBubbleIntroduction( self.play(PiCreatureBubbleIntroduction(
morty, "Suppose you \\\\ don't know this.", morty, "Suppose you \\\\ don't know this.",
)) ))
to_fade =self.get_mobjects_from_last_animation()
self.play(Blink(morty)) self.play(Blink(morty))
self.dither() self.dither()
self.play(*map( self.play(*map(FadeOut, to_fade))
FadeOut, [morty, morty.bubble, morty.bubble.content]
))
self.play(*map(FadeOut, [radial_line, perp_mark])) self.play(*map(FadeOut, [radial_line, perp_mark]))
self.dither() self.dither()
@ -317,13 +312,15 @@ class SlopeOfCircleExample(ZoomedScene):
dy_dx = TexMobject("\\frac{dy}{dx}") dy_dx = TexMobject("\\frac{dy}{dx}")
VGroup(*dy_dx[:2]).highlight(RED) VGroup(*dy_dx[:2]).highlight(RED)
VGroup(*dy_dx[-2:]).highlight(GREEN) VGroup(*dy_dx[-2:]).highlight(GREEN)
dy_dx.next_to(new_slope_word, RIGHT, buff = SMALL_BUFF) dy_dx.next_to(new_slope_word, RIGHT)
dy_dx.add_background_rectangle() dy_dx.add_background_rectangle()
self.play(Transform(slope_word, new_slope_word)) self.play(Transform(slope_word, new_slope_word))
self.play(Write(dy_dx)) self.play(Write(dy_dx))
self.dither() self.dither()
self.dy_dx = dy_dx
def point_out_this_is_not_a_graph(self): def point_out_this_is_not_a_graph(self):
equation = self.circle_equation equation = self.circle_equation
x = equation[1][0] x = equation[1][0]
@ -364,13 +361,370 @@ class SlopeOfCircleExample(ZoomedScene):
def perform_implicit_derivative(self): def perform_implicit_derivative(self):
equation = self.circle_equation equation = self.circle_equation
morty = Mortimer()
morty.flip()
morty.next_to(ORIGIN, LEFT)
morty.to_edge(DOWN, buff = SMALL_BUFF)
q_marks = TexMobject("???")
q_marks.next_to(morty, UP)
def show_rearrangement(self): rect = Rectangle(
pass width = SPACE_WIDTH - SMALL_BUFF,
height = SPACE_HEIGHT - SMALL_BUFF,
stroke_width = 0,
fill_color = BLACK,
fill_opacity = 0.8,
)
rect.to_corner(DOWN+RIGHT, buff = 0)
derivative = TexMobject("2x\\,dx + 2y\\,dy = 0")
dx = VGroup(*derivative[2:4])
dy = VGroup(*derivative[7:9])
dx.highlight(GREEN)
dy.highlight(RED)
self.play(
FadeIn(rect),
FadeIn(morty),
equation.next_to, ORIGIN, DOWN, MED_LARGE_BUFF,
equation.shift, SPACE_WIDTH*RIGHT/2,
)
self.play(
morty.change_mode, "confused",
morty.look_at, equation
)
self.play(Blink(morty))
derivative.next_to(equation, DOWN)
derivative.shift(
equation[1][-3].get_center()[0]*RIGHT - \
derivative[-2].get_center()[0]*RIGHT
)
#Differentiate
self.play(
morty.look_at, derivative[0],
*[
ReplacementTransform(
equation[1][i].copy(),
derivative[j],
)
for i, j in (1, 0), (0, 1)
]
)
self.play(Write(dx, run_time = 1))
self.dither()
self.play(*[
ReplacementTransform(
equation[1][i].copy(),
derivative[j],
)
for i, j in (2, 4), (3, 6), (4, 5)
])
self.play(Write(dy, run_time = 1))
self.play(Blink(morty))
self.play(*[
ReplacementTransform(
equation[1][i].copy(),
derivative[j],
)
for i, j in (-3, -2), (-2, -1), (-1, -1)
])
self.dither()
#React
self.play(morty.change_mode, "erm")
self.play(Blink(morty))
self.play(Write(q_marks))
self.dither()
self.play(Indicate(dx), morty.look_at, dx)
self.play(Indicate(dy), morty.look_at, dy)
self.dither()
self.play(
morty.change_mode, "shruggie",
FadeOut(q_marks)
)
self.play(Blink(morty))
self.play(
morty.change_mode, "pondering",
morty.look_at, derivative,
)
#Rearrange
x, y, eq = np.array(derivative)[[1, 6, 9]]
final_form = TexMobject(
"\\frac{dy}{dx} = \\frac{-x}{y}"
)
new_dy = VGroup(*final_form[:2])
new_dx = VGroup(*final_form[3:5])
new_dy.highlight(dy.get_color())
new_dx.highlight(dx.get_color())
new_dy.add(final_form[2])
new_x = VGroup(*final_form[6:8])
new_y = VGroup(*final_form[8:10])
new_eq = final_form[5]
final_form.next_to(derivative, DOWN)
final_form.shift((eq.get_center()[0]-new_eq.get_center()[0])*RIGHT)
self.play(*[
ReplacementTransform(
mover.copy(), target,
run_time = 2,
path_arc = np.pi/2,
)
for mover, target in [
(dy, new_dy),
(dx, new_dx),
(eq, new_eq),
(x, new_x),
(y, new_y)
]
] + [
morty.look_at, final_form
])
self.dither(2)
self.morty = morty
self.neg_x_over_y = VGroup(*final_form[6:])
def show_final_slope(self): def show_final_slope(self):
pass morty = self.morty
dy_dx = self.dy_dx
coords = self.example_point_coords_mob
x, y = coords[1][1].copy(), coords[1][3].copy()
frac = self.neg_x_over_y.copy()
frac.generate_target()
eq = TexMobject("=")
eq.add_background_rectangle()
eq.next_to(dy_dx, RIGHT)
frac.target.next_to(eq, RIGHT)
frac.target.shift(SMALL_BUFF*DOWN)
rect = BackgroundRectangle(frac.target)
self.play(
FadeIn(rect),
MoveToTarget(frac),
Write(eq),
morty.look_at, rect,
run_time = 2,
)
self.dither()
self.play(FocusOn(coords), morty.look_at, coords)
self.play(Indicate(coords))
scale_factor = 1.4
self.play(
x.scale, scale_factor,
x.highlight, GREEN,
x.move_to, frac[1],
FadeOut(frac[1]),
y.scale, scale_factor,
y.highlight, RED,
y.move_to, frac[3], DOWN,
y.shift, SMALL_BUFF*UP,
FadeOut(frac[3]),
morty.look_at, frac,
run_time = 2
)
self.dither()
self.play(Blink(morty))
class NameImplicitDifferentation(TeacherStudentsScene):
def construct(self):
title = TextMobject("``Implicit differentiation''")
equation = TexMobject("x^2", "+", "y^2", "=", "5^2")
derivative = TexMobject(
"2x\\,dx", "+", "2y\\,dy", "=", "0"
)
VGroup(*derivative[0][2:]).highlight(GREEN)
VGroup(*derivative[2][2:]).highlight(RED)
arrow = Arrow(ORIGIN, DOWN, buff = SMALL_BUFF)
group = VGroup(title, equation, arrow, derivative)
group.arrange_submobjects(DOWN)
group.to_edge(UP)
self.add(title, equation)
self.play(
self.get_teacher().change_mode, "raise_right_hand",
ShowCreation(arrow)
)
self.change_student_modes(
*["confused"]*3,
look_at_arg = derivative,
added_anims = [ReplacementTransform(equation.copy(), derivative)]
)
self.dither(2)
self.teacher_says(
"Don't worry...",
added_anims = [
group.scale, 0.7,
group.to_corner, UP+LEFT,
]
)
self.change_student_modes(*["happy"]*3)
self.dither(3)
class Ladder(VMobject):
CONFIG = {
"height" : 4,
"width" : 1,
"n_rungs" : 6,
}
def generate_points(self):
left_line, right_line = [
Line(ORIGIN, self.height*UP).shift(self.width*vect/2.0)
for vect in LEFT, RIGHT
]
rungs = [
Line(
left_line.point_from_proportion(a),
right_line.point_from_proportion(a),
)
for a in np.linspace(0, 1, self.n_rungs+2)[1:-1]
]
self.add(left_line, right_line, *rungs)
self.center()
class RelatedRatesExample(ThreeDScene):
CONFIG = {
"start_x" : 3.0,
"start_y" : 4.0,
"wall_color" : color_gradient([GREY_BROWN, BLACK], 4)[1],
"wall_center" : LEFT,
}
def construct(self):
should_skip_animations = self.skip_animations
self.skip_animations = True
self.introduce_ladder()
self.write_related_rates()
self.measure_ladder()
self.skip_animations = should_skip_animations
self.slide_ladder()
def introduce_ladder(self):
ladder = Ladder(height = self.get_ladder_length())
wall = Prism(
dimensions = [0.5, 6, 5],
fill_color = self.wall_color,
fill_opacity = 1,
)
wall.rotate(np.pi/12, UP)
wall.shift(self.wall_center)
ladder.generate_target()
ladder.fallen = ladder.copy()
ladder.target.rotate(self.get_ladder_angle(), LEFT)
ladder.fallen.rotate(np.pi/2, LEFT)
for ladder_copy in ladder.target, ladder.fallen:
ladder_copy.rotate(-5*np.pi/12, UP)
ladder_copy.next_to(wall, LEFT, 0, DOWN)
ladder_copy.shift(LARGE_BUFF*RIGHT)
self.play(
ShowCreation(ladder, run_time = 2)
)
self.dither()
self.play(
DrawBorderThenFill(wall),
MoveToTarget(ladder),
run_time = 2
)
self.dither()
self.ladder = ladder
def write_related_rates(self):
words = TextMobject("Related rates")
words.to_corner(UP+RIGHT)
self.play(Write(words))
self.dither()
def measure_ladder(self):
ladder = self.ladder
ladder_brace = self.get_ladder_brace(ladder)
x_and_y_lines = self.get_x_and_y_lines(ladder)
x_line, y_line = x_and_y_lines
y_label = TexMobject("%dm"%int(self.start_y))
y_label.next_to(y_line, LEFT, buff = SMALL_BUFF)
y_label.highlight(y_line.get_color())
x_label = TexMobject("%dm"%int(self.start_x))
x_label.next_to(x_line, UP)
x_label.highlight(x_line.get_color())
self.play(
GrowFromCenter(ladder_brace),
Write(ladder_brace.length_label),
)
self.dither()
self.play(ShowCreation(y_line), Write(y_label))
self.dither()
self.play(ShowCreation(x_line), Write(x_label))
self.dither(2)
self.play(*map(FadeOut, [x_label, y_label]))
self.ladder_brace = ladder_brace
self.x_and_y_lines = x_and_y_lines
def slide_ladder(self):
ladder = self.ladder
brace = self.ladder_brace
x_and_y_lines = self.x_and_y_lines
self.play(
Transform(
ladder, ladder.fallen,
rate_func = None,
run_time = self.start_y,
),
)
self.dither()
#########
def get_ladder_brace(self, ladder):
vect = rotate_vector(LEFT, -self.get_ladder_angle())
brace = Brace(ladder, vect)
length_string = "%dm"%int(self.get_ladder_length())
length_label = brace.get_text(length_string)
brace.length_label = length_label
return brace
def get_x_and_y_lines(self, ladder):
top_point = ladder.get_corner(UP+RIGHT)
bottom_point = ladder.get_corner(DOWN+LEFT)
interim_point = top_point[0]*RIGHT + bottom_point[1]*UP
y_line = Line(top_point, interim_point)
y_line.highlight(RED)
x_line = Line(bottom_point, interim_point)
x_line.highlight(GREEN)
return VGroup(x_line, y_line)
def get_ladder_angle(self):
if hasattr(self, "ladder"):
c1 = self.ladder.get_corner(UP+RIGHT)
c2 = self.ladder.get_corner(DOWN+LEFT)
vect = c1-c2
return np.pi/2 - angle_of_vector(vect)
else:
return np.arctan(self.start_x/self.start_y)
def get_ladder_length(self):
return np.linalg.norm([self.start_x, self.start_y])

View File

@ -292,7 +292,7 @@ class Mobject(object):
self.do_in_place(self.stretch, factor, dim) self.do_in_place(self.stretch, factor, dim)
return self return self
def stretch_to_fit(self, length, dim, stretch = True): def rescale_to_fit(self, length, dim, stretch = False):
old_length = self.length_over_dim(dim) old_length = self.length_over_dim(dim)
if old_length == 0: if old_length == 0:
return self return self
@ -303,16 +303,16 @@ class Mobject(object):
return self return self
def stretch_to_fit_width(self, width): def stretch_to_fit_width(self, width):
return self.stretch_to_fit(width, 0) return self.rescale_to_fit(width, 0, stretch = True)
def stretch_to_fit_height(self, height): def stretch_to_fit_height(self, height):
return self.stretch_to_fit(height, 1) return self.rescale_to_fit(height, 1, stretch = True)
def scale_to_fit_width(self, width): def scale_to_fit_width(self, width):
return self.stretch_to_fit(width, 0, stretch = False) return self.rescale_to_fit(width, 0, stretch = False)
def scale_to_fit_height(self, height): def scale_to_fit_height(self, height):
return self.stretch_to_fit(height, 1, stretch = False) return self.rescale_to_fit(height, 1, stretch = False)
def space_out_submobjects(self, factor = 1.5, **kwargs): def space_out_submobjects(self, factor = 1.5, **kwargs):
self.scale_in_place(factor) self.scale_in_place(factor)
@ -337,7 +337,7 @@ class Mobject(object):
self.stretch_to_fit_width(mobject.get_width()) self.stretch_to_fit_width(mobject.get_width())
self.stretch_to_fit_height(mobject.get_height()) self.stretch_to_fit_height(mobject.get_height())
else: else:
self.stretch_to_fit( self.rescale_to_fit(
mobject.length_over_dim(dim_to_match), mobject.length_over_dim(dim_to_match),
dim_to_match, dim_to_match,
stretch = False stretch = False

View File

@ -171,7 +171,7 @@ class Brace(TexMobject):
mob.rotate(angle) mob.rotate(angle)
def put_at_tip(self, mob, **kwargs): def put_at_tip(self, mob, **kwargs):
mob.next_to(self, self.direction, **kwargs) mob.next_to(self.get_tip(), self.get_direction(), **kwargs)
return self return self
def get_text(self, *text, **kwargs): def get_text(self, *text, **kwargs):
@ -179,6 +179,17 @@ class Brace(TexMobject):
self.put_at_tip(text_mob, **kwargs) self.put_at_tip(text_mob, **kwargs)
return text_mob return text_mob
def get_tip(self):
# Very specific to the LaTeX representation
# of a brace, but it's the only way I can think
# of to get the tip regardless of orientation.
return self.submobjects[2].get_anchors()[7]
def get_direction(self):
vect = self.get_tip() - self.get_center()
return vect/np.linalg.norm(vect)
def tex_hash(expression, template_tex_file): def tex_hash(expression, template_tex_file):

View File

@ -24,7 +24,7 @@ class CameraWithPerspective(Camera):
class ThreeDCamera(CameraWithPerspective): class ThreeDCamera(CameraWithPerspective):
CONFIG = { CONFIG = {
"sun_vect" : 3*UP+LEFT, "sun_vect" : 5*UP+LEFT,
"shading_factor" : 0.5, "shading_factor" : 0.5,
} }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -66,18 +66,16 @@ class ThreeDCamera(CameraWithPerspective):
def display_multiple_vectorized_mobjects(self, vmobjects): def display_multiple_vectorized_mobjects(self, vmobjects):
def z_cmp(*vmobs): def z_cmp(*vmobs):
is_three_d = np.array([ #Compare to three dimensional mobjects based on their
hasattr(vm, "part_of_three_d_mobject") #z value, otherwise don't compare.
for vm in vmobs is_three_d = [hasattr(vm, "part_of_3d_mobject") for vm in vmobs]
]) has_points = [vm.get_num_points() > 0 for vm in vmobs]
if sum(is_three_d) == 2: if all(is_three_d) and all(has_points):
cmp_vect = self.get_unit_normal_vect(vmobs[0]) cmp_vect = self.get_unit_normal_vect(vm)
return cmp(*[ return cmp(*[
np.dot(vm.get_center(), cmp_vect) np.dot(vm.get_center(), cmp_vect)
for vm in vmobs for vm in vmobs
]) ])
elif sum(is_three_d) == 1:
return 1 if is_three_d[0] else -1
else: else:
return 0 return 0
Camera.display_multiple_vectorized_mobjects( Camera.display_multiple_vectorized_mobjects(
@ -95,7 +93,7 @@ class ThreeDMobject(VMobject):
def __init__(self, **kwargs): def __init__(self, **kwargs):
VMobject.__init__(self, **kwargs) VMobject.__init__(self, **kwargs)
for submobject in self.submobject_family(): for submobject in self.submobject_family():
submobject.part_of_three_d_mobject = True submobject.part_of_3d_mobject = True
class Cube(ThreeDMobject): class Cube(ThreeDMobject):
CONFIG = { CONFIG = {
@ -113,7 +111,14 @@ class Cube(ThreeDMobject):
self.add(face) self.add(face)
class Prism(Cube):
CONFIG = {
"dimensions" : [3, 2, 1]
}
def generate_points(self):
Cube.generate_points(self)
for dim, value in enumerate(self.dimensions):
self.rescale_to_fit(value, dim, stretch = True)