mirror of
https://github.com/3b1b/manim.git
synced 2025-07-30 13:34:19 +08:00
Partway through derivative of sine explanation
This commit is contained in:
770
eoc/chapter3.py
770
eoc/chapter3.py
@ -1402,7 +1402,7 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
"graph_origin" : (SPACE_WIDTH - LARGE_BUFF)*LEFT + 2*DOWN,
|
||||
"rectangle_color_kwargs" : {
|
||||
"fill_color" : BLUE,
|
||||
"fill_opacity" : 0.7,
|
||||
"fill_opacity" : 0.5,
|
||||
"stroke_width" : 1,
|
||||
"stroke_color" : WHITE,
|
||||
},
|
||||
@ -1419,6 +1419,9 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
"morty_scale_val" : 0.8,
|
||||
"area_label_scale_factor" : 0.75,
|
||||
"dx" : 0.1,
|
||||
"start_x_value" : 1.3,
|
||||
"dx_color" : GREEN,
|
||||
"df_color" : RED,
|
||||
}
|
||||
def setup(self):
|
||||
for c in self.__class__.__bases__:
|
||||
@ -1426,14 +1429,11 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
self.x_max = self.x_axis_width/self.unit_length
|
||||
self.y_max = self.y_axis_height/self.unit_length
|
||||
|
||||
|
||||
def construct(self):
|
||||
self.introduce_function()
|
||||
self.introduce_puddle()
|
||||
self.introduce_graph()
|
||||
self.perform_nudge()
|
||||
# self.draw_graph()
|
||||
|
||||
|
||||
def introduce_function(self):
|
||||
func = TexMobject("f(x) = ", "\\frac{1}{x}")
|
||||
@ -1464,7 +1464,7 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
path_arc = np.pi
|
||||
)
|
||||
self.play(FadeIn(neg_two))
|
||||
self.dither(2)
|
||||
self.dither()
|
||||
self.say(
|
||||
"More geometry!",
|
||||
target_mode = "hooray",
|
||||
@ -1477,15 +1477,198 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
self.dither()
|
||||
self.play(*self.get_bubble_fade_anims())
|
||||
|
||||
|
||||
def introduce_puddle(self):
|
||||
pass
|
||||
rect_group = self.get_rectangle_group(self.start_x_value)
|
||||
|
||||
self.play(
|
||||
DrawBorderThenFill(rect_group.rectangle),
|
||||
Write(rect_group.area_label),
|
||||
self.pi_creature.change_mode, "thinking"
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(rect_group.x_brace),
|
||||
Write(rect_group.x_label),
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(rect_group.recip_brace),
|
||||
Write(rect_group.recip_label),
|
||||
)
|
||||
self.setup_axes(animate = True)
|
||||
self.dither()
|
||||
|
||||
for d in 2, 3:
|
||||
self.change_rectangle_group(
|
||||
rect_group, d,
|
||||
target_group_kwargs = {
|
||||
"x_label" : str(d),
|
||||
"one_over_x_label" : "\\frac{1}{%d}"%d,
|
||||
},
|
||||
run_time = 2
|
||||
)
|
||||
self.dither()
|
||||
self.change_rectangle_group(rect_group, 3)
|
||||
self.dither()
|
||||
|
||||
self.rect_group = rect_group
|
||||
|
||||
def introduce_graph(self):
|
||||
pass
|
||||
rect_group = self.rect_group
|
||||
graph = self.get_graph(lambda x : 1./x)
|
||||
graph.points = np.array(list(reversed(graph.points)))
|
||||
|
||||
self.change_rectangle_group(
|
||||
rect_group, 0.01,
|
||||
added_anims = [
|
||||
ShowCreation(graph)
|
||||
],
|
||||
run_time = 5,
|
||||
)
|
||||
self.change_mode("happy")
|
||||
self.change_rectangle_group(rect_group, self.start_x_value)
|
||||
self.dither()
|
||||
|
||||
self.graph = graph
|
||||
|
||||
def perform_nudge(self):
|
||||
pass
|
||||
rect_group = self.rect_group
|
||||
graph = self.graph
|
||||
|
||||
rect_copy = rect_group.rectangle.copy()
|
||||
rect_copy.set_fill(opacity = 0)
|
||||
new_rect = self.get_rectangle(
|
||||
self.start_x_value + self.dx
|
||||
)
|
||||
|
||||
recip_brace = rect_group.recip_brace
|
||||
recip_brace.generate_target()
|
||||
recip_brace.target.next_to(
|
||||
new_rect, RIGHT,
|
||||
buff = SMALL_BUFF,
|
||||
aligned_edge = DOWN,
|
||||
)
|
||||
recip_label = rect_group.recip_label
|
||||
recip_label.generate_target()
|
||||
recip_label.target.next_to(recip_brace.target, RIGHT)
|
||||
|
||||
h_lines = VGroup(*[
|
||||
DashedLine(
|
||||
ORIGIN, (rect_copy.get_width()+LARGE_BUFF)*RIGHT,
|
||||
color = self.df_color,
|
||||
stroke_width = 2
|
||||
).move_to(rect.get_corner(UP+LEFT), LEFT)
|
||||
for rect in rect_group.rectangle, new_rect
|
||||
])
|
||||
|
||||
v_lines = VGroup(*[
|
||||
DashedLine(
|
||||
ORIGIN, (rect_copy.get_height()+MED_LARGE_BUFF)*UP,
|
||||
color = self.dx_color,
|
||||
stroke_width = 2
|
||||
).move_to(rect.get_corner(DOWN+RIGHT), DOWN)
|
||||
for rect in rect_group.rectangle, new_rect
|
||||
])
|
||||
|
||||
dx_brace = Brace(v_lines, UP, buff = 0)
|
||||
dx_label = dx_brace.get_text("$dx$")
|
||||
dx_brace.add(dx_label)
|
||||
|
||||
df_brace = Brace(h_lines, RIGHT, buff = 0)
|
||||
df_label = df_brace.get_text("$d\\left(\\frac{1}{x}\\right)$")
|
||||
df_brace.add(df_label)
|
||||
minus_sign = TexMobject("-")
|
||||
minus_sign.move_to(df_label, LEFT)
|
||||
|
||||
area_changes = VGroup()
|
||||
point_pairs = [
|
||||
(new_rect.get_corner(UP+RIGHT), rect_copy.get_corner(DOWN+RIGHT)),
|
||||
(new_rect.get_corner(UP+LEFT), rect_copy.get_corner(UP+RIGHT))
|
||||
]
|
||||
for color, point_pair in zip([self.dx_color, self.df_color], point_pairs):
|
||||
area_change_rect = Rectangle(
|
||||
fill_opacity = 1,
|
||||
fill_color = color,
|
||||
stroke_width = 0
|
||||
)
|
||||
area_change_rect.replace(
|
||||
VGroup(*map(VectorizedPoint, point_pair)),
|
||||
stretch = True
|
||||
)
|
||||
area_changes.add(area_change_rect)
|
||||
area_gained, area_lost = area_changes
|
||||
|
||||
area_gained_label = TextMobject("Area gained")
|
||||
area_gained_label.scale(0.75)
|
||||
area_gained_label.next_to(
|
||||
rect_copy.get_corner(DOWN+RIGHT),
|
||||
UP+LEFT, buff = SMALL_BUFF
|
||||
)
|
||||
area_gained_arrow = Arrow(
|
||||
area_gained_label.get_top(),
|
||||
area_gained.get_center(),
|
||||
buff = 0,
|
||||
color = WHITE
|
||||
)
|
||||
|
||||
area_lost_label = TextMobject("Area lost")
|
||||
area_lost_label.scale(0.75)
|
||||
area_lost_label.next_to(rect_copy.get_left(), RIGHT)
|
||||
area_lost_arrow = Arrow(
|
||||
area_lost_label.get_top(),
|
||||
area_lost.get_center(),
|
||||
buff = 0,
|
||||
color = WHITE
|
||||
)
|
||||
|
||||
question = TexMobject(
|
||||
"\\frac{d(1/x)}{dx} = ???"
|
||||
)
|
||||
question.next_to(
|
||||
self.pi_creature.get_corner(UP+LEFT),
|
||||
UP, buff = MED_SMALL_BUFF,
|
||||
)
|
||||
|
||||
self.play(
|
||||
FadeOut(rect_group.area_label),
|
||||
ReplacementTransform(rect_copy, new_rect),
|
||||
MoveToTarget(recip_brace),
|
||||
MoveToTarget(recip_label),
|
||||
self.pi_creature.change_mode, "pondering"
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(dx_brace),
|
||||
*map(ShowCreation, v_lines)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
GrowFromCenter(df_brace),
|
||||
*map(ShowCreation, h_lines)
|
||||
)
|
||||
self.change_mode("confused")
|
||||
self.dither()
|
||||
|
||||
self.play(
|
||||
FadeIn(area_gained),
|
||||
Write(area_gained_label, run_time = 2),
|
||||
ShowCreation(area_gained_arrow)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
FadeIn(area_lost),
|
||||
Write(area_lost_label, run_time = 2),
|
||||
ShowCreation(area_lost_arrow)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
Write(minus_sign),
|
||||
df_label.next_to, minus_sign, RIGHT, SMALL_BUFF
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
Write(question),
|
||||
self.pi_creature.change_mode, "raise_right_hand"
|
||||
)
|
||||
self.dither(2)
|
||||
|
||||
|
||||
########
|
||||
@ -1525,7 +1708,7 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
result.x_val = x
|
||||
result.rectangle = self.get_rectangle(x)
|
||||
|
||||
result.x_brace, result.dx_brace = braces = [
|
||||
result.x_brace, result.recip_brace = braces = [
|
||||
Brace(result.rectangle, vect)
|
||||
for vect in UP, RIGHT
|
||||
]
|
||||
@ -1533,6 +1716,7 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
for brace, label in zip(braces, [x_label, one_over_x_label]):
|
||||
brace.get_text("$%s$"%label)
|
||||
result.labels.add(brace.get_text("$%s$"%label))
|
||||
result.x_label, result.recip_label = result.labels
|
||||
|
||||
area_label = TextMobject("Area = 1")
|
||||
area_label.scale(self.area_label_scale_factor)
|
||||
@ -1545,16 +1729,21 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
result.add(
|
||||
result.rectangle,
|
||||
result.x_brace,
|
||||
result.dx_brace,
|
||||
result.recip_brace,
|
||||
result.labels,
|
||||
result.area_label,
|
||||
)
|
||||
return result
|
||||
|
||||
def get_rectangle(self, x):
|
||||
try:
|
||||
y = 1./x
|
||||
except ZeroDivisionError:
|
||||
y = 100
|
||||
|
||||
rectangle = Rectangle(
|
||||
width = x*self.unit_length,
|
||||
height = (1./x)*self.unit_length,
|
||||
height = y*self.unit_length,
|
||||
**self.rectangle_color_kwargs
|
||||
)
|
||||
rectangle.move_to(self.graph_origin, DOWN+LEFT)
|
||||
@ -1593,21 +1782,548 @@ class OneOverX(PiCreatureScene, GraphScene):
|
||||
*added_anims,
|
||||
**anim_kwargs
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
rect_group.x_val = target_x
|
||||
|
||||
class SquareRootOfX(Scene):
|
||||
CONFIG = {
|
||||
"square_color_kwargs" : {
|
||||
"stroke_color" : WHITE,
|
||||
"stroke_width" : 1,
|
||||
"fill_color" : BLUE_E,
|
||||
"fill_opacity" : 1,
|
||||
},
|
||||
"bigger_square_color_kwargs" : {
|
||||
"stroke_color" : WHITE,
|
||||
"stroke_width" : 1,
|
||||
"fill_color" : YELLOW,
|
||||
"fill_opacity" : 0.7,
|
||||
},
|
||||
"square_corner" : 6*LEFT+3*UP,
|
||||
"square_width" : 3,
|
||||
"d_sqrt_x" : 0.3,
|
||||
}
|
||||
def construct(self):
|
||||
self.add_title()
|
||||
self.introduce_square()
|
||||
self.nudge_square()
|
||||
|
||||
def add_title(self):
|
||||
title = TexMobject("f(x) = \\sqrt{x}")
|
||||
title.next_to(ORIGIN, RIGHT)
|
||||
title.to_edge(UP)
|
||||
self.add(title)
|
||||
|
||||
def introduce_square(self):
|
||||
square = Square(
|
||||
side_length = self.square_width,
|
||||
**self.square_color_kwargs
|
||||
)
|
||||
square.move_to(self.square_corner, UP+LEFT)
|
||||
area_label = TextMobject("Area $ = x$")
|
||||
area_label.move_to(square)
|
||||
|
||||
bottom_brace, right_brace = braces = VGroup(*[
|
||||
Brace(square, vect)
|
||||
for vect in DOWN, RIGHT
|
||||
])
|
||||
for brace in braces:
|
||||
brace.add(brace.get_text("$\\sqrt{x}$"))
|
||||
|
||||
|
||||
self.play(
|
||||
DrawBorderThenFill(square),
|
||||
Write(area_label)
|
||||
)
|
||||
self.play(*map(FadeIn, braces))
|
||||
self.dither()
|
||||
|
||||
self.square = square
|
||||
self.area_label = area_label
|
||||
self.braces = braces
|
||||
|
||||
def nudge_square(self):
|
||||
square = self.square
|
||||
area_label = self.area_label
|
||||
bottom_brace, right_brace = self.braces
|
||||
|
||||
bigger_square = Square(
|
||||
side_length = self.square_width + self.d_sqrt_x,
|
||||
**self.bigger_square_color_kwargs
|
||||
)
|
||||
bigger_square.move_to(self.square_corner, UP+LEFT)
|
||||
|
||||
square_copy = square.copy()
|
||||
|
||||
lines = VGroup(*[
|
||||
DashedLine(
|
||||
ORIGIN,
|
||||
(self.square_width + MED_LARGE_BUFF)*vect,
|
||||
color = WHITE,
|
||||
stroke_width = 3
|
||||
).shift(s.get_corner(corner))
|
||||
for corner, vect in [(DOWN+LEFT, RIGHT), (UP+RIGHT, DOWN)]
|
||||
for s in square, bigger_square
|
||||
])
|
||||
little_braces = VGroup(*[
|
||||
Brace(VGroup(*line_pair), vect, buff = 0)
|
||||
for line_pair, vect in (lines[:2], RIGHT), (lines[2:], DOWN)
|
||||
])
|
||||
for brace in little_braces:
|
||||
tex = brace.get_text("$d\\sqrt{x}$", buff = SMALL_BUFF)
|
||||
tex.scale_in_place(0.8)
|
||||
brace.add(tex)
|
||||
|
||||
area_increase = TextMobject("$dx$ = New area")
|
||||
area_increase.highlight(bigger_square.get_color())
|
||||
area_increase.next_to(square, RIGHT, 4)
|
||||
|
||||
question = TexMobject("\\frac{d\\sqrt{x}}{dx} = ???")
|
||||
VGroup(*question[5:7]).highlight(bigger_square.get_color())
|
||||
question.next_to(
|
||||
area_increase, DOWN,
|
||||
aligned_edge = LEFT,
|
||||
buff = LARGE_BUFF
|
||||
)
|
||||
|
||||
self.play(
|
||||
Transform(square_copy, bigger_square),
|
||||
Animation(square),
|
||||
Animation(area_label),
|
||||
bottom_brace.next_to, bigger_square, DOWN, SMALL_BUFF, LEFT,
|
||||
right_brace.next_to, bigger_square, RIGHT, SMALL_BUFF, UP,
|
||||
)
|
||||
self.play(Write(area_increase))
|
||||
self.play(*it.chain(
|
||||
map(ShowCreation, lines),
|
||||
map(FadeIn, little_braces)
|
||||
))
|
||||
self.play(Write(question))
|
||||
self.dither()
|
||||
|
||||
class MentionSine(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.teacher_says("Let's tackle $\\sin(\\theta)$")
|
||||
self.change_student_modes("pondering", "hooray", "erm")
|
||||
self.dither(2)
|
||||
self.student_thinks("")
|
||||
self.zoom_in_on_thought_bubble()
|
||||
|
||||
class IntroduceUnitCircleWithSine(GraphScene):
|
||||
CONFIG = {
|
||||
"unit_length" : 2.5,
|
||||
"graph_origin" : ORIGIN,
|
||||
"x_axis_width" : 15,
|
||||
"y_axis_height" : 10,
|
||||
"x_min" : -3,
|
||||
"x_max" : 3,
|
||||
"y_min" : -2,
|
||||
"y_max" : 2,
|
||||
"x_labeled_nums" : [-2, -1, 1, 2],
|
||||
"y_labeled_nums" : [-1, 1],
|
||||
"x_tick_frequency" : 0.5,
|
||||
"y_tick_frequency" : 0.5,
|
||||
"circle_color" : BLUE,
|
||||
"example_radians" : 0.8,
|
||||
"rotations_per_second" : 0.25,
|
||||
"include_radial_line_dot" : True,
|
||||
"remove_angle_label" : True,
|
||||
"line_class" : DashedLine,
|
||||
"theta_label" : "= 0.8",
|
||||
}
|
||||
def construct(self):
|
||||
self.setup_axes()
|
||||
self.add_title()
|
||||
self.introduce_unit_circle()
|
||||
self.draw_example_radians()
|
||||
self.label_sine()
|
||||
self.walk_around_circle()
|
||||
|
||||
def add_title(self):
|
||||
title = TexMobject("f(\\theta) = \\sin(\\theta)")
|
||||
title.to_corner(UP+LEFT)
|
||||
self.add(title)
|
||||
|
||||
def introduce_unit_circle(self):
|
||||
circle = self.get_unit_circle()
|
||||
radial_line = Line(ORIGIN, self.unit_length*RIGHT)
|
||||
radial_line.highlight(RED)
|
||||
if self.include_radial_line_dot:
|
||||
dot = Dot()
|
||||
dot.move_to(radial_line.get_end())
|
||||
radial_line.add(dot)
|
||||
|
||||
self.play(ShowCreation(radial_line))
|
||||
self.play(
|
||||
ShowCreation(circle),
|
||||
Rotate(radial_line, 2*np.pi),
|
||||
run_time = 2,
|
||||
)
|
||||
self.dither()
|
||||
|
||||
self.circle = circle
|
||||
self.radial_line = radial_line
|
||||
|
||||
def draw_example_radians(self):
|
||||
circle = self.circle
|
||||
radial_line = self.radial_line
|
||||
|
||||
line = Line(
|
||||
ORIGIN, self.example_radians*self.unit_length*UP,
|
||||
color = YELLOW,
|
||||
)
|
||||
line.shift(SPACE_WIDTH*RIGHT/3).to_edge(UP)
|
||||
line.insert_n_anchor_points(10)
|
||||
line.make_smooth()
|
||||
|
||||
arc = Arc(
|
||||
self.example_radians, radius = self.unit_length,
|
||||
color = line.get_color(),
|
||||
)
|
||||
arc_copy = arc.copy().highlight(WHITE)
|
||||
|
||||
brace = Brace(line, RIGHT)
|
||||
brace_text = brace.get_text("$\\theta%s$"%self.theta_label)
|
||||
brace_text.highlight(line.get_color())
|
||||
theta_copy = brace_text[0].copy()
|
||||
|
||||
self.play(
|
||||
GrowFromCenter(line),
|
||||
GrowFromCenter(brace),
|
||||
)
|
||||
self.play(Write(brace_text))
|
||||
self.dither()
|
||||
self.play(
|
||||
line.move_to, radial_line.get_end(), DOWN,
|
||||
FadeOut(brace)
|
||||
)
|
||||
self.play(ReplacementTransform(line, arc))
|
||||
self.dither()
|
||||
self.play(
|
||||
Rotate(radial_line, self.example_radians),
|
||||
ShowCreation(arc_copy)
|
||||
)
|
||||
self.dither()
|
||||
arc_copy.generate_target()
|
||||
arc_copy.target.scale(0.2)
|
||||
theta_copy.generate_target()
|
||||
theta_copy.target.next_to(
|
||||
arc_copy.target, RIGHT,
|
||||
aligned_edge = DOWN,
|
||||
buff = SMALL_BUFF
|
||||
)
|
||||
theta_copy.target.shift(SMALL_BUFF*UP)
|
||||
self.play(*map(MoveToTarget, [arc_copy, theta_copy]))
|
||||
self.dither()
|
||||
|
||||
self.angle_label = VGroup(arc_copy, theta_copy)
|
||||
self.example_theta_equation = brace_text
|
||||
|
||||
def label_sine(self):
|
||||
radial_line = self.radial_line
|
||||
|
||||
drop_point = radial_line.get_end()[0]*RIGHT
|
||||
v_line = self.line_class(radial_line.get_end(), drop_point)
|
||||
brace = Brace(v_line, RIGHT)
|
||||
brace_text = brace.get_text("$\\sin(\\theta)$")
|
||||
brace_text[-2].highlight(YELLOW)
|
||||
|
||||
self.play(ShowCreation(v_line))
|
||||
self.play(
|
||||
GrowFromCenter(brace),
|
||||
Write(brace_text)
|
||||
)
|
||||
self.dither()
|
||||
faders = [brace, brace_text, self.example_theta_equation]
|
||||
if self.remove_angle_label:
|
||||
faders += self.angle_label
|
||||
self.play(*map(FadeOut, faders))
|
||||
|
||||
self.v_line = v_line
|
||||
|
||||
def walk_around_circle(self):
|
||||
radial_line = self.radial_line
|
||||
v_line = self.v_line
|
||||
|
||||
def v_line_update(v_line):
|
||||
drop_point = radial_line.get_end()[0]*RIGHT
|
||||
v_line.put_start_and_end_on(
|
||||
radial_line.get_end(), drop_point
|
||||
)
|
||||
return v_line
|
||||
filler_arc = self.circle.copy()
|
||||
filler_arc.highlight(YELLOW)
|
||||
curr_arc_portion = self.example_radians/(2*np.pi)
|
||||
filler_portion = 1 - curr_arc_portion
|
||||
filler_arc.pointwise_become_partial(filler_arc, curr_arc_portion, 1)
|
||||
|
||||
self.play(
|
||||
Rotate(radial_line, filler_portion*2*np.pi),
|
||||
ShowCreation(filler_arc),
|
||||
UpdateFromFunc(v_line, v_line_update),
|
||||
run_time = filler_portion/self.rotations_per_second,
|
||||
rate_func = None,
|
||||
)
|
||||
for x in range(5):
|
||||
self.play(
|
||||
Rotate(radial_line, 2*np.pi),
|
||||
UpdateFromFunc(v_line, v_line_update),
|
||||
run_time = 1./self.rotations_per_second,
|
||||
rate_func = None,
|
||||
)
|
||||
|
||||
##############
|
||||
|
||||
def setup_axes(self):
|
||||
GraphScene.setup_axes(self)
|
||||
VGroup(*self.x_axis.numbers[:2]).shift(MED_SMALL_BUFF*LEFT)
|
||||
VGroup(*self.x_axis.numbers[2:]).shift(SMALL_BUFF*RIGHT)
|
||||
self.y_axis.numbers[0].shift(MED_SMALL_BUFF*DOWN)
|
||||
self.y_axis.numbers[1].shift(MED_SMALL_BUFF*UP)
|
||||
|
||||
def get_unit_circle(self):
|
||||
return Circle(
|
||||
radius = self.unit_length,
|
||||
color = self.circle_color,
|
||||
)
|
||||
|
||||
class DerivativeIntuitionFromSineGraph(GraphScene):
|
||||
CONFIG = {
|
||||
"graph_origin" : 6*LEFT,
|
||||
"x_axis_width" : 11,
|
||||
"x_min" : 0,
|
||||
"x_max" : 4*np.pi,
|
||||
"x_labeled_nums" : np.arange(0, 4*np.pi, np.pi),
|
||||
"x_tick_frequency" : np.pi/4,
|
||||
"x_axis_label" : "$\\theta$",
|
||||
"y_axis_height" : 6,
|
||||
"y_min" : -2,
|
||||
"y_max" : 2,
|
||||
"y_tick_frequency" : 0.5,
|
||||
"y_axis_label" : "",
|
||||
}
|
||||
def construct(self):
|
||||
self.setup_axes()
|
||||
self.draw_sine_graph()
|
||||
self.draw_derivative_from_slopes()
|
||||
|
||||
def draw_sine_graph(self):
|
||||
graph = self.get_graph(np.sin)
|
||||
v_line = DashedLine(ORIGIN, UP)
|
||||
rps = IntroduceUnitCircleWithSine.CONFIG["rotations_per_second"]
|
||||
self.play(
|
||||
ShowCreation(graph),
|
||||
UpdateFromFunc(v_line, lambda v : self.v_line_update(v, graph)),
|
||||
run_time = 2./rps,
|
||||
rate_func = None
|
||||
)
|
||||
self.dither()
|
||||
|
||||
|
||||
|
||||
self.graph = graph
|
||||
|
||||
def draw_derivative_from_slopes(self):
|
||||
ss_group = self.get_secant_slope_group(
|
||||
0, self.graph,
|
||||
dx = 0.01,
|
||||
secant_line_color = RED,
|
||||
)
|
||||
deriv_graph = self.get_graph(np.cos, color = DERIVATIVE_COLOR)
|
||||
v_line = DashedLine(
|
||||
self.graph_origin, self.coords_to_point(0, 1),
|
||||
color = RED
|
||||
)
|
||||
|
||||
self.play(ShowCreation(ss_group, submobject_mode = "all_at_once"))
|
||||
self.play(ShowCreation(v_line))
|
||||
self.dither()
|
||||
last_theta = 0
|
||||
next_theta = np.pi/2
|
||||
while last_theta < self.x_max:
|
||||
deriv_copy = deriv_graph.copy()
|
||||
self.animate_secant_slope_group_change(
|
||||
ss_group,
|
||||
target_x = next_theta,
|
||||
added_anims = [
|
||||
ShowCreation(
|
||||
deriv_copy,
|
||||
rate_func = lambda t : interpolate(
|
||||
last_theta/self.x_max,
|
||||
next_theta/self.x_max,
|
||||
smooth(t)
|
||||
),
|
||||
run_time = 3,
|
||||
),
|
||||
UpdateFromFunc(
|
||||
v_line,
|
||||
lambda v : self.v_line_update(v, deriv_copy),
|
||||
run_time = 3
|
||||
),
|
||||
]
|
||||
)
|
||||
self.dither()
|
||||
if next_theta == 2*np.pi:
|
||||
words = TextMobject("Looks a lot like $\\cos(\\theta)$")
|
||||
words.next_to(self.graph_origin, RIGHT)
|
||||
words.to_edge(UP)
|
||||
arrow = Arrow(
|
||||
words.get_bottom(),
|
||||
deriv_graph.point_from_proportion(0.45)
|
||||
)
|
||||
VGroup(words, arrow).highlight(deriv_graph.get_color())
|
||||
self.play(
|
||||
Write(words),
|
||||
ShowCreation(arrow)
|
||||
)
|
||||
self.remove(deriv_copy)
|
||||
last_theta = next_theta
|
||||
next_theta += np.pi/2
|
||||
self.add(deriv_copy)
|
||||
|
||||
######
|
||||
|
||||
def v_line_update(self, v_line, graph):
|
||||
point = graph.point_from_proportion(1)
|
||||
drop_point = point[0]*RIGHT
|
||||
v_line.put_start_and_end_on(drop_point, point)
|
||||
return v_line
|
||||
|
||||
def setup_axes(self):
|
||||
GraphScene.setup_axes(self)
|
||||
self.x_axis.remove(self.x_axis.numbers)
|
||||
self.remove(self.x_axis.numbers)
|
||||
for x in range(1, 4):
|
||||
if x == 1:
|
||||
label = TexMobject("\\pi")
|
||||
else:
|
||||
label = TexMobject("%d\\pi"%x)
|
||||
label.next_to(self.coords_to_point(x*np.pi, 0), DOWN, MED_LARGE_BUFF)
|
||||
self.add(label)
|
||||
self.x_axis_label_mob.highlight(YELLOW)
|
||||
|
||||
class DerivativeFromZoomingInOnSine(IntroduceUnitCircleWithSine, ZoomedScene):
|
||||
CONFIG = {
|
||||
"zoom_factor" : 10,
|
||||
"zoomed_canvas_space_shape" : (3, 4.5),
|
||||
"include_radial_line_dot" : False,
|
||||
"remove_angle_label" : False,
|
||||
"theta_label" : "",
|
||||
"line_class" : Line,
|
||||
"example_radians" : 1.0,
|
||||
"zoomed_canvas_corner_buff" : SMALL_BUFF,
|
||||
"d_theta" : 0.05,
|
||||
}
|
||||
def construct(self):
|
||||
self.setup_axes()
|
||||
self.add_title()
|
||||
self.introduce_unit_circle()
|
||||
self.draw_example_radians()
|
||||
self.label_sine()
|
||||
|
||||
self.zoom_in()
|
||||
self.perform_nudge()
|
||||
self.show_similar_triangles()
|
||||
self.analyze_ratios()
|
||||
|
||||
def zoom_in(self):
|
||||
self.activate_zooming()
|
||||
self.little_rectangle.next_to(self.radial_line.get_end(), UP, LARGE_BUFF)
|
||||
self.play(*map(FadeIn, [
|
||||
self.little_rectangle, self.big_rectangle
|
||||
]))
|
||||
self.play(
|
||||
self.little_rectangle.move_to,
|
||||
self.radial_line.get_end(), DOWN+RIGHT,
|
||||
self.little_rectangle.shift,
|
||||
SMALL_BUFF*(DOWN+RIGHT)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
def perform_nudge(self):
|
||||
d_theta_arc = Arc(
|
||||
start_angle = self.example_radians,
|
||||
angle = self.d_theta,
|
||||
radius = self.unit_length,
|
||||
color = MAROON_B,
|
||||
stroke_width = 6
|
||||
)
|
||||
d_theta_arc.scale(self.zoom_factor)
|
||||
d_theta_brace = Brace(
|
||||
d_theta_arc,
|
||||
rotate_vector(RIGHT, self.example_radians)
|
||||
)
|
||||
d_theta_label = TexMobject("d\\theta")
|
||||
d_theta_label.next_to(
|
||||
d_theta_brace.get_center(), d_theta_brace.direction,
|
||||
MED_LARGE_BUFF
|
||||
)
|
||||
d_theta_label.highlight(d_theta_arc.get_color())
|
||||
|
||||
group = VGroup(d_theta_arc, d_theta_brace, d_theta_label)
|
||||
group.scale(1./self.zoom_factor)
|
||||
|
||||
point = self.radial_line.get_end()
|
||||
nudged_point = d_theta_arc.point_from_proportion(1)
|
||||
# new_v_line = self.line_class(
|
||||
# nudged_point, nudged_point[0]*RIGHT
|
||||
# )
|
||||
interim_point = nudged_point[0]*RIGHT+point[1]*UP
|
||||
h_line = DashedLine(
|
||||
interim_point, point,
|
||||
dashed_segment_length = 0.01
|
||||
)
|
||||
d_sine_line = Line(interim_point, nudged_point, color = DERIVATIVE_COLOR)
|
||||
d_sine_brace = Brace(Line(ORIGIN, UP), LEFT)
|
||||
d_sine_brace.scale_to_fit_height(d_sine_line.get_height())
|
||||
d_sine_brace.next_to(d_sine_line, LEFT, buff = SMALL_BUFF/self.zoom_factor)
|
||||
d_sine = TexMobject("d(\\sin(\\theta))")
|
||||
d_sine.highlight(d_sine_line.get_color())
|
||||
d_sine.scale_to_fit_width(1.5*self.d_theta*self.unit_length)
|
||||
d_sine.next_to(d_sine_brace, LEFT, SMALL_BUFF/self.zoom_factor)
|
||||
|
||||
self.play(
|
||||
ShowCreation(d_theta_arc),
|
||||
# ReplacementTransform(self.v_line.copy(), new_v_line)
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(d_theta_brace),
|
||||
FadeIn(d_theta_label)
|
||||
)
|
||||
self.dither()
|
||||
self.play(
|
||||
ShowCreation(h_line),
|
||||
ShowCreation(d_sine_line)
|
||||
)
|
||||
self.play(
|
||||
GrowFromCenter(d_sine_brace),
|
||||
Write(d_sine)
|
||||
)
|
||||
self.dither()
|
||||
|
||||
self.little_triangle = Polygon(
|
||||
nudged_point, point, interim_point
|
||||
)
|
||||
|
||||
|
||||
def show_similar_triangles(self):
|
||||
little_triangle = self.little_triangle
|
||||
big_triangle = Polygon(
|
||||
self.graph_origin,
|
||||
self.radial_line.get_end(),
|
||||
self.radial_line.get_end()[0]*RIGHT,
|
||||
)
|
||||
for triangle in little_triangle, big_triangle:
|
||||
triangle.highlight(GREEN)
|
||||
triangle.set_fill(GREEN, opacity = 0.5)
|
||||
new_angle_label = self.angle_label.copy()
|
||||
# new_angle_label.
|
||||
|
||||
self.add(little_triangle, big_triangle)
|
||||
|
||||
|
||||
|
||||
def analyze_ratios(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
@ -17,14 +17,14 @@ class GraphScene(Scene):
|
||||
"x_axis_width" : 9,
|
||||
"x_tick_frequency" : 1,
|
||||
"x_leftmost_tick" : None, #Change if different from x_min
|
||||
"x_labeled_nums" : range(1, 10),
|
||||
"x_labeled_nums" : None,
|
||||
"x_axis_label" : "$x$",
|
||||
"y_min" : -1,
|
||||
"y_max" : 10,
|
||||
"y_axis_height" : 6,
|
||||
"y_tick_frequency" : 1,
|
||||
"y_bottom_tick" : None, #Change if different from y_min
|
||||
"y_labeled_nums" : range(1, 10),
|
||||
"y_labeled_nums" : None,
|
||||
"y_axis_label" : "$y$",
|
||||
"axes_color" : GREY,
|
||||
"graph_origin" : 2.5*DOWN + 4*LEFT,
|
||||
@ -37,6 +37,10 @@ class GraphScene(Scene):
|
||||
def setup_axes(self, animate = False):
|
||||
x_num_range = float(self.x_max - self.x_min)
|
||||
self.space_unit_to_x = self.x_axis_width/x_num_range
|
||||
if self.x_labeled_nums is None:
|
||||
self.x_labeled_nums = np.arange(
|
||||
self.x_min, self.x_max, 2*self.x_tick_frequency
|
||||
)
|
||||
x_axis = NumberLine(
|
||||
x_min = self.x_min,
|
||||
x_max = self.x_max,
|
||||
@ -46,8 +50,8 @@ class GraphScene(Scene):
|
||||
numbers_with_elongated_ticks = self.x_labeled_nums,
|
||||
color = self.axes_color
|
||||
)
|
||||
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
||||
if self.x_labeled_nums:
|
||||
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
||||
if len(self.x_labeled_nums) > 0:
|
||||
x_axis.add_numbers(*filter(
|
||||
lambda x : x != 0,
|
||||
self.x_labeled_nums
|
||||
@ -64,6 +68,10 @@ class GraphScene(Scene):
|
||||
|
||||
y_num_range = float(self.y_max - self.y_min)
|
||||
self.space_unit_to_y = self.y_axis_height/y_num_range
|
||||
if self.y_labeled_nums is None:
|
||||
self.y_labeled_nums = np.arange(
|
||||
self.y_min, self.y_max, 2*self.y_tick_frequency
|
||||
)
|
||||
y_axis = NumberLine(
|
||||
x_min = self.y_min,
|
||||
x_max = self.y_max,
|
||||
@ -75,7 +83,7 @@ class GraphScene(Scene):
|
||||
)
|
||||
y_axis.shift(self.graph_origin-y_axis.number_to_point(0))
|
||||
y_axis.rotate(np.pi/2, about_point = y_axis.number_to_point(0))
|
||||
if self.y_labeled_nums:
|
||||
if len(self.y_labeled_nums) > 0:
|
||||
y_axis.add_numbers(*filter(
|
||||
lambda y : y != 0,
|
||||
self.y_labeled_nums
|
||||
|
@ -126,7 +126,6 @@ class TextMobject(TexMobject):
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Brace(TexMobject):
|
||||
CONFIG = {
|
||||
"buff" : 0.2,
|
||||
|
@ -16,6 +16,7 @@ class ZoomedScene(Scene):
|
||||
"zoomed_canvas_space_shape" : (3, 3),
|
||||
"zoomed_canvas_center" : None,
|
||||
"zoomed_canvas_corner" : UP+RIGHT,
|
||||
"zoomed_canvas_corner_buff" : DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
||||
"zoomed_camera_background" : None,
|
||||
"zoom_factor" : 6,
|
||||
"square_color" : WHITE,
|
||||
@ -47,7 +48,10 @@ class ZoomedScene(Scene):
|
||||
if self.zoomed_canvas_center is not None:
|
||||
self.big_rectangle.shift(self.zoomed_canvas_center)
|
||||
elif self.zoomed_canvas_corner is not None:
|
||||
self.big_rectangle.to_corner(self.zoomed_canvas_corner)
|
||||
self.big_rectangle.to_corner(
|
||||
self.zoomed_canvas_corner,
|
||||
buff = self.zoomed_canvas_corner_buff
|
||||
)
|
||||
self.add(self.big_rectangle)
|
||||
|
||||
|
||||
@ -60,8 +64,8 @@ class ZoomedScene(Scene):
|
||||
self.zoomed_canvas_pixel_indices = pixel_coords
|
||||
(up, left), (down, right) = pixel_coords
|
||||
self.zoomed_canvas_pixel_shape = (
|
||||
right-left,
|
||||
down-up,
|
||||
right-left
|
||||
)
|
||||
|
||||
def setup_zoomed_camera(self):
|
||||
|
@ -123,10 +123,12 @@ class Line(VMobject):
|
||||
return angle_of_vector(end-start)
|
||||
|
||||
def put_start_and_end_on(self, new_start, new_end):
|
||||
epsilon = 0.01
|
||||
if self.get_length() == 0:
|
||||
#TODO, this is hacky
|
||||
self.points[0] += 0.01*LEFT
|
||||
self.points[0] += epsilon*LEFT
|
||||
new_length = np.linalg.norm(new_end - new_start)
|
||||
new_length = max(new_length, epsilon)
|
||||
new_angle = angle_of_vector(new_end - new_start)
|
||||
self.scale(new_length / self.get_length())
|
||||
self.rotate(new_angle - self.get_angle())
|
||||
|
Reference in New Issue
Block a user