mirror of
https://github.com/3b1b/manim.git
synced 2025-08-01 08:54:38 +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,
|
"graph_origin" : (SPACE_WIDTH - LARGE_BUFF)*LEFT + 2*DOWN,
|
||||||
"rectangle_color_kwargs" : {
|
"rectangle_color_kwargs" : {
|
||||||
"fill_color" : BLUE,
|
"fill_color" : BLUE,
|
||||||
"fill_opacity" : 0.7,
|
"fill_opacity" : 0.5,
|
||||||
"stroke_width" : 1,
|
"stroke_width" : 1,
|
||||||
"stroke_color" : WHITE,
|
"stroke_color" : WHITE,
|
||||||
},
|
},
|
||||||
@ -1419,6 +1419,9 @@ class OneOverX(PiCreatureScene, GraphScene):
|
|||||||
"morty_scale_val" : 0.8,
|
"morty_scale_val" : 0.8,
|
||||||
"area_label_scale_factor" : 0.75,
|
"area_label_scale_factor" : 0.75,
|
||||||
"dx" : 0.1,
|
"dx" : 0.1,
|
||||||
|
"start_x_value" : 1.3,
|
||||||
|
"dx_color" : GREEN,
|
||||||
|
"df_color" : RED,
|
||||||
}
|
}
|
||||||
def setup(self):
|
def setup(self):
|
||||||
for c in self.__class__.__bases__:
|
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.x_max = self.x_axis_width/self.unit_length
|
||||||
self.y_max = self.y_axis_height/self.unit_length
|
self.y_max = self.y_axis_height/self.unit_length
|
||||||
|
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.introduce_function()
|
self.introduce_function()
|
||||||
self.introduce_puddle()
|
self.introduce_puddle()
|
||||||
self.introduce_graph()
|
self.introduce_graph()
|
||||||
self.perform_nudge()
|
self.perform_nudge()
|
||||||
# self.draw_graph()
|
|
||||||
|
|
||||||
|
|
||||||
def introduce_function(self):
|
def introduce_function(self):
|
||||||
func = TexMobject("f(x) = ", "\\frac{1}{x}")
|
func = TexMobject("f(x) = ", "\\frac{1}{x}")
|
||||||
@ -1464,7 +1464,7 @@ class OneOverX(PiCreatureScene, GraphScene):
|
|||||||
path_arc = np.pi
|
path_arc = np.pi
|
||||||
)
|
)
|
||||||
self.play(FadeIn(neg_two))
|
self.play(FadeIn(neg_two))
|
||||||
self.dither(2)
|
self.dither()
|
||||||
self.say(
|
self.say(
|
||||||
"More geometry!",
|
"More geometry!",
|
||||||
target_mode = "hooray",
|
target_mode = "hooray",
|
||||||
@ -1477,15 +1477,198 @@ class OneOverX(PiCreatureScene, GraphScene):
|
|||||||
self.dither()
|
self.dither()
|
||||||
self.play(*self.get_bubble_fade_anims())
|
self.play(*self.get_bubble_fade_anims())
|
||||||
|
|
||||||
|
|
||||||
def introduce_puddle(self):
|
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):
|
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):
|
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.x_val = x
|
||||||
result.rectangle = self.get_rectangle(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)
|
Brace(result.rectangle, vect)
|
||||||
for vect in UP, RIGHT
|
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]):
|
for brace, label in zip(braces, [x_label, one_over_x_label]):
|
||||||
brace.get_text("$%s$"%label)
|
brace.get_text("$%s$"%label)
|
||||||
result.labels.add(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 = TextMobject("Area = 1")
|
||||||
area_label.scale(self.area_label_scale_factor)
|
area_label.scale(self.area_label_scale_factor)
|
||||||
@ -1545,16 +1729,21 @@ class OneOverX(PiCreatureScene, GraphScene):
|
|||||||
result.add(
|
result.add(
|
||||||
result.rectangle,
|
result.rectangle,
|
||||||
result.x_brace,
|
result.x_brace,
|
||||||
result.dx_brace,
|
result.recip_brace,
|
||||||
result.labels,
|
result.labels,
|
||||||
result.area_label,
|
result.area_label,
|
||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_rectangle(self, x):
|
def get_rectangle(self, x):
|
||||||
|
try:
|
||||||
|
y = 1./x
|
||||||
|
except ZeroDivisionError:
|
||||||
|
y = 100
|
||||||
|
|
||||||
rectangle = Rectangle(
|
rectangle = Rectangle(
|
||||||
width = x*self.unit_length,
|
width = x*self.unit_length,
|
||||||
height = (1./x)*self.unit_length,
|
height = y*self.unit_length,
|
||||||
**self.rectangle_color_kwargs
|
**self.rectangle_color_kwargs
|
||||||
)
|
)
|
||||||
rectangle.move_to(self.graph_origin, DOWN+LEFT)
|
rectangle.move_to(self.graph_origin, DOWN+LEFT)
|
||||||
@ -1593,21 +1782,548 @@ class OneOverX(PiCreatureScene, GraphScene):
|
|||||||
*added_anims,
|
*added_anims,
|
||||||
**anim_kwargs
|
**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_axis_width" : 9,
|
||||||
"x_tick_frequency" : 1,
|
"x_tick_frequency" : 1,
|
||||||
"x_leftmost_tick" : None, #Change if different from x_min
|
"x_leftmost_tick" : None, #Change if different from x_min
|
||||||
"x_labeled_nums" : range(1, 10),
|
"x_labeled_nums" : None,
|
||||||
"x_axis_label" : "$x$",
|
"x_axis_label" : "$x$",
|
||||||
"y_min" : -1,
|
"y_min" : -1,
|
||||||
"y_max" : 10,
|
"y_max" : 10,
|
||||||
"y_axis_height" : 6,
|
"y_axis_height" : 6,
|
||||||
"y_tick_frequency" : 1,
|
"y_tick_frequency" : 1,
|
||||||
"y_bottom_tick" : None, #Change if different from y_min
|
"y_bottom_tick" : None, #Change if different from y_min
|
||||||
"y_labeled_nums" : range(1, 10),
|
"y_labeled_nums" : None,
|
||||||
"y_axis_label" : "$y$",
|
"y_axis_label" : "$y$",
|
||||||
"axes_color" : GREY,
|
"axes_color" : GREY,
|
||||||
"graph_origin" : 2.5*DOWN + 4*LEFT,
|
"graph_origin" : 2.5*DOWN + 4*LEFT,
|
||||||
@ -37,6 +37,10 @@ class GraphScene(Scene):
|
|||||||
def setup_axes(self, animate = False):
|
def setup_axes(self, animate = False):
|
||||||
x_num_range = float(self.x_max - self.x_min)
|
x_num_range = float(self.x_max - self.x_min)
|
||||||
self.space_unit_to_x = self.x_axis_width/x_num_range
|
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_axis = NumberLine(
|
||||||
x_min = self.x_min,
|
x_min = self.x_min,
|
||||||
x_max = self.x_max,
|
x_max = self.x_max,
|
||||||
@ -46,8 +50,8 @@ class GraphScene(Scene):
|
|||||||
numbers_with_elongated_ticks = self.x_labeled_nums,
|
numbers_with_elongated_ticks = self.x_labeled_nums,
|
||||||
color = self.axes_color
|
color = self.axes_color
|
||||||
)
|
)
|
||||||
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
x_axis.shift(self.graph_origin - x_axis.number_to_point(0))
|
||||||
if self.x_labeled_nums:
|
if len(self.x_labeled_nums) > 0:
|
||||||
x_axis.add_numbers(*filter(
|
x_axis.add_numbers(*filter(
|
||||||
lambda x : x != 0,
|
lambda x : x != 0,
|
||||||
self.x_labeled_nums
|
self.x_labeled_nums
|
||||||
@ -64,6 +68,10 @@ class GraphScene(Scene):
|
|||||||
|
|
||||||
y_num_range = float(self.y_max - self.y_min)
|
y_num_range = float(self.y_max - self.y_min)
|
||||||
self.space_unit_to_y = self.y_axis_height/y_num_range
|
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(
|
y_axis = NumberLine(
|
||||||
x_min = self.y_min,
|
x_min = self.y_min,
|
||||||
x_max = self.y_max,
|
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.shift(self.graph_origin-y_axis.number_to_point(0))
|
||||||
y_axis.rotate(np.pi/2, about_point = 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(
|
y_axis.add_numbers(*filter(
|
||||||
lambda y : y != 0,
|
lambda y : y != 0,
|
||||||
self.y_labeled_nums
|
self.y_labeled_nums
|
||||||
|
@ -126,7 +126,6 @@ class TextMobject(TexMobject):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Brace(TexMobject):
|
class Brace(TexMobject):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"buff" : 0.2,
|
"buff" : 0.2,
|
||||||
|
@ -16,6 +16,7 @@ class ZoomedScene(Scene):
|
|||||||
"zoomed_canvas_space_shape" : (3, 3),
|
"zoomed_canvas_space_shape" : (3, 3),
|
||||||
"zoomed_canvas_center" : None,
|
"zoomed_canvas_center" : None,
|
||||||
"zoomed_canvas_corner" : UP+RIGHT,
|
"zoomed_canvas_corner" : UP+RIGHT,
|
||||||
|
"zoomed_canvas_corner_buff" : DEFAULT_MOBJECT_TO_EDGE_BUFFER,
|
||||||
"zoomed_camera_background" : None,
|
"zoomed_camera_background" : None,
|
||||||
"zoom_factor" : 6,
|
"zoom_factor" : 6,
|
||||||
"square_color" : WHITE,
|
"square_color" : WHITE,
|
||||||
@ -47,7 +48,10 @@ class ZoomedScene(Scene):
|
|||||||
if self.zoomed_canvas_center is not None:
|
if self.zoomed_canvas_center is not None:
|
||||||
self.big_rectangle.shift(self.zoomed_canvas_center)
|
self.big_rectangle.shift(self.zoomed_canvas_center)
|
||||||
elif self.zoomed_canvas_corner is not None:
|
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)
|
self.add(self.big_rectangle)
|
||||||
|
|
||||||
|
|
||||||
@ -60,8 +64,8 @@ class ZoomedScene(Scene):
|
|||||||
self.zoomed_canvas_pixel_indices = pixel_coords
|
self.zoomed_canvas_pixel_indices = pixel_coords
|
||||||
(up, left), (down, right) = pixel_coords
|
(up, left), (down, right) = pixel_coords
|
||||||
self.zoomed_canvas_pixel_shape = (
|
self.zoomed_canvas_pixel_shape = (
|
||||||
|
right-left,
|
||||||
down-up,
|
down-up,
|
||||||
right-left
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def setup_zoomed_camera(self):
|
def setup_zoomed_camera(self):
|
||||||
|
@ -123,10 +123,12 @@ class Line(VMobject):
|
|||||||
return angle_of_vector(end-start)
|
return angle_of_vector(end-start)
|
||||||
|
|
||||||
def put_start_and_end_on(self, new_start, new_end):
|
def put_start_and_end_on(self, new_start, new_end):
|
||||||
|
epsilon = 0.01
|
||||||
if self.get_length() == 0:
|
if self.get_length() == 0:
|
||||||
#TODO, this is hacky
|
#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 = np.linalg.norm(new_end - new_start)
|
||||||
|
new_length = max(new_length, epsilon)
|
||||||
new_angle = angle_of_vector(new_end - new_start)
|
new_angle = angle_of_vector(new_end - new_start)
|
||||||
self.scale(new_length / self.get_length())
|
self.scale(new_length / self.get_length())
|
||||||
self.rotate(new_angle - self.get_angle())
|
self.rotate(new_angle - self.get_angle())
|
||||||
|
Reference in New Issue
Block a user