Added (somewhat large) ProveEllipse scene

This commit is contained in:
Grant Sanderson 2018-07-14 10:32:39 -07:00
parent a76bea6d72
commit 0f9e1796c7

View file

@ -122,14 +122,7 @@ class ShowEmergingEllipse(Scene):
rot_words = TextMobject("Rotate $90^\\circ$ \\\\ about center")
rot_words.next_to(line_dot, RIGHT)
elbow = VGroup(Line(UP, UL), Line(UL, LEFT))
elbow.set_stroke(width=1)
elbow.scale(0.2, about_point=ORIGIN)
elbow.rotate(
line.get_angle() - 90 * DEGREES,
about_point=ORIGIN
)
elbow.shift(line.get_center())
elbow = self.get_elbow(line)
eccentric_words = TextMobject("``Eccentric'' point")
eccentric_words.next_to(circle.get_center(), DOWN)
@ -223,6 +216,17 @@ class ShowEmergingEllipse(Scene):
width=self.ghost_lines_stroke_width
)
def get_elbow(self, line):
elbow = VGroup(Line(UP, UL), Line(UL, LEFT))
elbow.set_stroke(width=1)
elbow.scale(0.2, about_point=ORIGIN)
elbow.rotate(
line.get_angle() - 90 * DEGREES,
about_point=ORIGIN
)
elbow.shift(line.get_center())
return elbow
def get_ellipse(self):
center = self.circle.get_center()
e_point = self.get_eccentricity_point()
@ -331,7 +335,7 @@ class FeynmanFame(Scene):
objects = VGroup(safe, bongo)
feynman_smile = ImageMobject("Feynman_Los_Alamos")
feynman_smile.match_width(objects)
feynman_smile.scale_to_fit_height(4)
feynman_smile.next_to(objects, DOWN)
VGroup(objects, feynman_smile).next_to(ORIGIN, RIGHT)
@ -902,40 +906,909 @@ class AskAboutInfiniteIntelligence(TeacherStudentsScene):
class ShowEllipseDefiningProperty(Scene):
CONFIG = {
"camera_config": {"background_opacity": 1},
"ellipse_color": BLUE,
"a": 4.0,
"b": 3.0,
"distance_labels_scale_factor": 1.0,
}
def construct(self):
self.add_ellipse()
self.add_focal_lines()
self.add_distance_labels()
self.show_constant_sum()
self.label_foci()
self.label_focal_sum()
def add_ellipse(self):
a = self.a
b = self.b
c = np.sqrt(a**2 - b**2)
ellipse = Circle(radius=a, color=self.ellipse_color)
ellipse.stretch(fdiv(b, a), dim=1)
self.foci = [c * LEFT, c * RIGHT]
ellipse.to_edge(LEFT, buff=LARGE_BUFF)
self.ellipse = ellipse
self.add(ellipse)
def add_focal_lines(self):
pass
push_pins = VGroup(*[
SVGMobject(
file_name="push_pin",
color=LIGHT_GREY,
fill_opacity=0.8,
height=0.5,
).move_to(point, DR).shift(0.05 * RIGHT)
for point in self.get_foci()
])
dot = Dot()
dot.scale(0.5)
position_tracker = ValueTracker(0.125)
dot_update = ContinualUpdateFromFunc(
dot,
lambda d: d.move_to(
self.ellipse.point_from_proportion(
position_tracker.get_value() % 1
)
)
)
position_tracker_wander = ContinualMovement(
position_tracker, rate=0.05,
)
lines, lines_update_animation = self.get_focal_lines_and_update(
self.get_foci, dot
)
self.add_foreground_mobjects(push_pins, dot)
self.add(dot_update)
self.play(LaggedStart(
FadeInAndShiftFromDirection, push_pins,
lambda m: (m, 2 * UP + LEFT),
run_time=1,
lag_ratio=0.75
))
self.play(ShowCreation(lines))
self.add(lines_update_animation)
self.add(position_tracker_wander)
self.wait(2)
self.position_tracker = position_tracker
self.focal_lines = lines
def add_distance_labels(self):
pass
lines = self.focal_lines
colors = [YELLOW, PINK]
def show_constant_sum(self):
pass
distance_labels, distance_labels_animation = \
self.get_distance_labels_and_update(lines, colors)
sum_expression, numbers, number_updates = \
self.get_sum_expression_and_update(
lines, colors, lambda mob: mob.to_corner(UR)
)
sum_expression_fading_rect = BackgroundRectangle(
sum_expression, fill_opacity=1
)
sum_rect = SurroundingRectangle(numbers[-1])
constant_words = TextMobject("Stays constant")
constant_words.next_to(sum_rect, DOWN, aligned_edge=RIGHT)
VGroup(sum_rect, constant_words).set_color(BLUE)
self.add(distance_labels_animation)
self.add(*number_updates)
self.add(sum_expression)
self.add_foreground_mobjects(sum_expression_fading_rect)
self.play(
VFadeIn(distance_labels),
FadeOut(sum_expression_fading_rect),
)
self.remove_foreground_mobject(sum_expression_fading_rect)
self.wait(7)
self.play(
ShowCreation(sum_rect),
Write(constant_words)
)
self.wait(7)
self.play(FadeOut(sum_rect), FadeOut(constant_words))
self.sum_expression = sum_expression
self.sum_rect = sum_rect
def label_foci(self):
pass
foci = self.get_foci()
focus_words = VGroup(*[
TextMobject("Focus").next_to(focus, DOWN)
for focus in foci
])
foci_word = TextMobject("Foci")
foci_word.move_to(focus_words)
foci_word.shift(MED_SMALL_BUFF * UP)
connecting_lines = VGroup(*[
Arrow(
foci_word.get_edge_center(-edge),
focus_word.get_edge_center(edge),
buff=MED_SMALL_BUFF,
stroke_width=2,
)
for focus_word, edge in zip(focus_words, [LEFT, RIGHT])
])
translation = TextMobject(
"``Foco'' $\\rightarrow$ Fireplace"
)
translation.to_edge(RIGHT)
translation.shift(UP)
sun = ImageMobject("sun", height=0.5)
sun.move_to(foci[0])
sun_animation = SunAnimation(sun)
self.play(FadeInFromDown(focus_words))
self.wait(2)
self.play(
ReplacementTransform(focus_words.copy(), foci_word),
)
self.play(*map(ShowCreation, connecting_lines))
for word in list(focus_words) + [foci_word]:
word.add_background_rectangle()
self.add_foreground_mobjects(word)
self.wait(4)
self.play(Write(translation))
self.wait(2)
self.play(GrowFromCenter(sun))
self.add(sun_animation)
self.wait(8)
def label_focal_sum(self):
sum_rect = self.sum_rect
focal_sum = TextMobject("``Focal sum''")
focal_sum.scale(1.5)
focal_sum.next_to(sum_rect, DOWN, aligned_edge=RIGHT)
VGroup(sum_rect, focal_sum).set_color(RED)
footnote = TextMobject(
"""
\\Large
*This happens to equal the longest distance
across the ellipse, so perhaps the more standard
terminology would be ``major axis'', but I want
some terminology that conveys the idea of adding
two distances to the foci.
""",
alignment="",
)
footnote.scale_to_fit_width(5)
footnote.to_corner(DR)
footnote.set_stroke(WHITE, 0.5)
self.play(FadeInFromDown(focal_sum))
self.play(Write(sum_rect))
self.wait()
self.play(FadeIn(footnote))
self.wait(2)
self.play(FadeOut(footnote))
self.wait(8)
# Helpers
def get_foci(self):
ellipse = self.ellipse
a = ellipse.get_width() / 2
b = ellipse.get_height() / 2
c = np.sqrt(a**2 - b**2)
center = ellipse.get_center()
return [
center + c * RIGHT,
center + c * LEFT,
]
def get_focal_lines_and_update(self, get_foci, focal_sum_point):
lines = VGroup(Line(LEFT, RIGHT), Line(LEFT, RIGHT))
lines.set_stroke(width=2)
def update_lines(lines):
foci = get_foci()
for line, focus in zip(lines, foci):
line.put_start_and_end_on(
focus, focal_sum_point.get_center()
)
lines[1].rotate(np.pi)
lines_update_animation = ContinualUpdateFromFunc(
lines, update_lines
)
return lines, lines_update_animation
def get_distance_labels_and_update(self, lines, colors):
distance_labels = VGroup(
DecimalNumber(0), DecimalNumber(0),
)
for label in distance_labels:
label.scale(self.distance_labels_scale_factor)
def update_distance_labels(labels):
for label, line, color in zip(labels, lines, colors):
angle = -line.get_angle()
if np.abs(angle) > 90 * DEGREES:
angle = 180 * DEGREES + angle
line.rotate(angle, about_point=ORIGIN)
new_decimal = DecimalNumber(line.get_length())
new_decimal.scale(
self.distance_labels_scale_factor
)
max_width = 0.6 * line.get_width()
if new_decimal.get_width() > max_width:
new_decimal.scale_to_fit_width(max_width)
new_decimal.next_to(line, UP, SMALL_BUFF)
new_decimal.set_color(color)
VGroup(new_decimal, line).rotate(
-angle, about_point=ORIGIN
)
label.submobjects = list(new_decimal.submobjects)
distance_labels_animation = ContinualUpdateFromFunc(
distance_labels, update_distance_labels
)
return distance_labels, distance_labels_animation
def get_sum_expression_and_update(self, lines, colors, sum_position_func):
sum_expression = TexMobject("0.00", "+", "0.00", "=", "0.00")
sum_position_func(sum_expression)
number_refs = sum_expression.get_parts_by_tex("0.00")
number_refs.set_fill(opacity=0)
numbers = VGroup(*[DecimalNumber(0) for ref in number_refs])
for number, color in zip(numbers, colors):
number.set_color(color)
# Not the most elegant...
number_updates = [
ContinualChangingDecimal(
numbers[0], lambda a: lines[0].get_length(),
position_update_func=lambda m: m.move_to(
number_refs[1], LEFT
)
),
ContinualChangingDecimal(
numbers[1], lambda a: lines[1].get_length(),
position_update_func=lambda m: m.move_to(
number_refs[0], LEFT
)
),
ContinualChangingDecimal(
numbers[2], lambda a: sum(map(Line.get_length, lines)),
position_update_func=lambda m: m.move_to(
number_refs[2], LEFT
)
),
]
return sum_expression, numbers, number_updates
class GeometryProofLand(Scene):
def construct(self):
word = TextMobject("Geometry proof land")
word.rotate(-90 * DEGREES)
word.scale(0.25)
word.shift(3 * RIGHT)
word.apply_complex_function(np.exp)
word.rotate(90 * DEGREES)
word.scale_to_fit_width(9)
word.center()
word.to_edge(UP)
colors = [
PINK, RED, YELLOW, GREEN, GREEN_A, BLUE,
MAROON_E, MAROON_B, YELLOW, BLUE,
]
word.set_color_by_gradient(*colors)
word_outlines = word.deepcopy()
word_outlines.set_fill(opacity=0)
word_outlines.set_stroke(WHITE, 1)
random.shuffle(colors)
word_outlines.set_color_by_gradient(*colors)
circles = VGroup()
for letter in word:
circle = Circle()
# circle = letter.copy()
circle.replace(letter, dim_to_match=1)
circle.scale(3)
circle.set_stroke(WHITE, 0)
circle.set_fill(letter.get_color(), 0)
circles.add(circle)
circle.target = letter
self.play(
LaggedStart(MoveToTarget, circles),
run_time=2
)
self.play(LaggedStart(
ShowCreationThenDestruction, word_outlines,
run_time=4
))
self.wait()
class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
CONFIG = {
"eccentricity_vector": 1.5 * RIGHT,
"ellipse_color": PINK,
"distance_labels_scale_factor": 0.7,
}
def construct(self):
self.setup_ellipse()
self.hypothesize_foci()
self.setup_and_show_focal_sum()
self.show_circle_radius()
self.limit_to_just_one_line()
self.look_at_perpendicular_bisector()
self.show_orbiting_planet()
def setup_ellipse(self):
circle = self.circle = self.get_circle()
circle.to_edge(LEFT)
ep = self.get_eccentricity_point()
ep_dot = self.ep_dot = Dot(ep, color=YELLOW)
lines = self.lines = self.get_lines()
for line in lines:
line.save_state()
ghost_lines = self.ghost_lines = self.get_ghost_lines(lines)
ellipse = self.ellipse = self.get_ellipse()
self.add(ghost_lines, circle, lines, ep_dot)
self.play(
LaggedStart(MoveToTarget, lines),
Animation(ep_dot),
)
self.play(ShowCreation(ellipse))
self.wait()
def hypothesize_foci(self):
circle = self.circle
ghost_lines = self.ghost_lines
ghost_lines_copy = ghost_lines.copy().set_stroke(YELLOW, 3)
center = circle.get_center()
center_dot = Dot(center, color=RED)
# ep = self.get_eccentricity_point()
ep_dot = self.ep_dot
dots = VGroup(center_dot, ep_dot)
center_label = TextMobject("Circle center")
ep_label = TextMobject("Eccentric point")
labels = VGroup(center_label, ep_label)
vects = [UL, DR]
arrows = VGroup()
for label, dot, vect in zip(labels, dots, vects):
label.next_to(dot, vect, MED_LARGE_BUFF)
label.match_color(dot)
label.add_to_back(
label.copy().set_stroke(BLACK, 5)
)
arrow = Arrow(
label.get_corner(-vect),
dot.get_corner(vect),
buff=SMALL_BUFF
)
arrow.match_color(dot)
arrow.add_to_back(arrow.copy().set_stroke(BLACK, 5))
arrows.add(arrow)
labels_target = labels.copy()
labels_target.arrange_submobjects(
DOWN, aligned_edge=LEFT
)
guess_start = TextMobject("Guess: Foci = ")
brace = Brace(labels_target, LEFT)
full_guess = VGroup(guess_start, brace, labels_target)
full_guess.arrange_submobjects(RIGHT)
full_guess.to_corner(UR)
self.play(
FadeInFromDown(labels[1]),
GrowArrow(arrows[1]),
)
self.play(LaggedStart(
ShowPassingFlash, ghost_lines_copy
))
self.wait()
self.play(ReplacementTransform(circle.copy(), center_dot))
self.add_foreground_mobjects(dots)
self.play(
FadeInFromDown(labels[0]),
GrowArrow(arrows[0]),
)
self.wait()
self.play(
Write(guess_start),
GrowFromCenter(brace),
run_time=1
)
self.play(
ReplacementTransform(labels.copy(), labels_target)
)
self.wait()
self.play(FadeOut(labels), FadeOut(arrows))
self.center_dot = center_dot
def setup_and_show_focal_sum(self):
circle = self.circle
ellipse = self.ellipse
focal_sum_point = VectorizedPoint()
focal_sum_point.move_to(circle.get_top())
dots = [self.ep_dot, self.center_dot]
colors = map(Mobject.get_color, dots)
def get_foci():
return map(Mobject.get_center, dots)
focal_lines, focal_lines_update_animation = \
self.get_focal_lines_and_update(get_foci, focal_sum_point)
distance_labels, distance_labels_update_animation = \
self.get_distance_labels_and_update(focal_lines, colors)
sum_expression, numbers, number_updates = \
self.get_sum_expression_and_update(
focal_lines, colors,
lambda mob: mob.to_edge(RIGHT).shift(UP)
)
to_add = self.focal_sum_things_to_add = [
focal_lines_update_animation,
distance_labels_update_animation,
sum_expression,
] + list(number_updates)
self.play(
ShowCreation(focal_lines),
Write(distance_labels),
FadeIn(sum_expression),
Write(numbers),
run_time=1
)
self.wait()
self.add(*to_add)
points = [
ellipse.get_bottom(),
circle.point_from_proportion(0.9),
ellipse.get_top(),
ellipse.point_from_proportion(0.7),
]
for point in points:
self.play(
focal_sum_point.move_to, point
)
self.wait()
self.remove(*to_add)
self.play(*map(FadeOut, [
focal_lines, distance_labels,
sum_expression, numbers
]))
self.set_variables_as_attrs(
focal_lines, focal_lines_update_animation,
focal_sum_point,
distance_labels, distance_labels_update_animation,
sum_expression,
numbers, number_updates
)
def show_circle_radius(self):
circle = self.circle
center = circle.get_center()
point = circle.get_right()
color = GREEN
radius = Line(center, point, color=color)
radius_measurement = DecimalNumber(radius.get_length())
radius_measurement.set_color(color)
radius_measurement.next_to(radius, UP, SMALL_BUFF)
radius_measurement.add_to_back(
radius_measurement.copy().set_stroke(BLACK, 5)
)
group = VGroup(radius, radius_measurement)
group.rotate(30 * DEGREES, about_point=center)
self.play(ShowCreation(radius))
self.play(Write(radius_measurement))
self.wait()
self.play(Rotating(
group,
rate_func=smooth,
run_time=7,
about_point=center
))
self.play(FadeOut(group))
def limit_to_just_one_line(self):
lines = self.lines
ghost_lines = self.ghost_lines
ep_dot = self.ep_dot
index = int(0.2 * len(lines))
line = lines[index]
ghost_line = ghost_lines[index]
to_fade = VGroup(*list(lines) + list(ghost_lines))
to_fade.remove(line, ghost_line)
P_dot = Dot(line.saved_state.get_end())
P_label = TexMobject("P")
P_label.next_to(P_dot, UP, SMALL_BUFF)
self.add_foreground_mobjects(self.ellipse)
self.play(LaggedStart(Restore, lines))
self.play(
FadeOut(to_fade),
line.set_stroke, {"width": 3},
ReplacementTransform(ep_dot.copy(), P_dot),
)
self.play(FadeInFromDown(P_label))
self.wait()
for l in lines:
l.generate_target()
l.target.rotate(
90 * DEGREES,
about_point=l.get_center()
)
self.set_variables_as_attrs(
line, ghost_line,
P_dot, P_label
)
def look_at_perpendicular_bisector(self):
# Alright, this method's gonna blow up. Let's go!
circle = self.circle
ellipse = self.ellipse
ellipse.save_state()
lines = self.lines
line = self.line
ghost_lines = self.ghost_lines
ghost_line = self.ghost_line
P_dot = self.P_dot
P_label = self.P_label
elbow = self.get_elbow(line)
self.play(
MoveToTarget(line, path_arc=90 * DEGREES),
ShowCreation(elbow)
)
# Perpendicular bisector label
label = TextMobject("``Perpendicular bisector''")
label.scale(0.75)
label.set_color(YELLOW)
label.next_to(ORIGIN, UP, MED_SMALL_BUFF)
label.add_background_rectangle()
angle = line.get_angle() + np.pi
label.rotate(angle, about_point=ORIGIN)
label.shift(line.get_center())
# Dot defining Q point
Q_dot = Dot(color=GREEN)
Q_dot.move_to(self.focal_sum_point)
focal_sum_point_animation = NormalAnimationAsContinualAnimation(
MaintainPositionRelativeTo(
self.focal_sum_point, Q_dot
)
)
self.add(focal_sum_point_animation)
Q_dot.move_to(line.point_from_proportion(0.9))
Q_dot.save_state()
Q_label = TexMobject("Q")
Q_label.scale(0.7)
Q_label.match_color(Q_dot)
Q_label.add_to_back(Q_label.copy().set_stroke(BLACK, 5))
Q_label.next_to(Q_dot, UL, buff=0)
Q_label_animation = NormalAnimationAsContinualAnimation(
MaintainPositionRelativeTo(Q_label, Q_dot)
)
# Pretty hacky...
def distance_label_shift_update(label):
line = self.focal_lines[0]
if line.get_end()[0] > line.get_start()[0]:
vect = label.get_center() - line.get_center()
label.shift(-2 * vect)
distance_label_shift_update_animation = ContinualUpdateFromFunc(
self.distance_labels[0],
distance_label_shift_update
)
self.focal_sum_things_to_add.append(
distance_label_shift_update_animation
)
# Define QP line
QP_line = Line(LEFT, RIGHT)
QP_line.match_style(self.focal_lines)
QP_line_update = ContinualUpdateFromFunc(
QP_line, lambda l: l.put_start_and_end_on(
Q_dot.get_center(), P_dot.get_center(),
)
)
QE_line = Line(LEFT, RIGHT)
QE_line.set_stroke(YELLOW, 3)
QE_line_update = ContinualUpdateFromFunc(
QE_line, lambda l: l.put_start_and_end_on(
Q_dot.get_center(),
self.get_eccentricity_point()
)
)
# Define similar triangles
triangles = VGroup(*[
Polygon(
Q_dot.get_center(),
line.get_center(),
end_point,
fill_opacity=1,
)
for end_point in [
P_dot.get_center(),
self.get_eccentricity_point()
]
])
triangles.set_color_by_gradient(PINK, GREEN)
triangles.set_stroke(WHITE, 2)
# Add even more distant label updates
def distance_label_rotate_update(label):
QE_line_update.update(0)
angle = QP_line.get_angle() - QE_line.get_angle()
label.rotate(angle, about_point=Q_dot.get_center())
return label
distance_label_rotate_update_animation = ContinualUpdateFromFunc(
self.distance_labels[0],
distance_label_rotate_update
)
# Hook up line to P to P_dot
radial_line = DashedLine(ORIGIN, 3 * RIGHT)
radial_line_update = UpdateFromFunc(
radial_line, lambda l: l.put_start_and_end_on(
circle.get_center(),
P_dot.get_center()
)
)
def put_dot_at_intersection(dot):
point = line_intersection(
line.get_start_and_end(),
radial_line.get_start_and_end()
)
dot.move_to(point)
return dot
keep_Q_dot_at_intersection = UpdateFromFunc(
Q_dot, put_dot_at_intersection
)
Q_dot.restore()
ghost_line_update_animation = UpdateFromFunc(
ghost_line, lambda l: l.put_start_and_end_on(
self.get_eccentricity_point(),
P_dot.get_center()
)
)
def update_perp_bisector(line):
line.scale(ghost_line.get_length() / line.get_length())
line.rotate(ghost_line.get_angle() - line.get_angle())
line.rotate(90 * DEGREES)
line.move_to(ghost_line)
perp_bisector_update_animation = UpdateFromFunc(
line, update_perp_bisector
)
elbow_update_animation = UpdateFromFunc(
elbow,
lambda e: Transform(e, self.get_elbow(line)).update(1)
)
P_dot_movement_updates = [
radial_line_update,
keep_Q_dot_at_intersection,
MaintainPositionRelativeTo(
P_label, P_dot
),
ghost_line_update_animation,
perp_bisector_update_animation,
elbow_update_animation,
]
# Comment for tangency
sum_rect = SurroundingRectangle(
self.numbers[-1]
)
tangency_comment = TextMobject(
"Always $\\ge$ radius"
)
tangency_comment.next_to(
sum_rect, DOWN,
aligned_edge=RIGHT
)
VGroup(sum_rect, tangency_comment).set_color(GREEN)
# Why is this needed?!?
self.add(*self.focal_sum_things_to_add)
self.wait(0)
self.remove(*self.focal_sum_things_to_add)
# Show label
self.play(Write(label))
self.wait()
# Show Q_dot moving about a little
self.play(
FadeOut(label),
FadeIn(self.focal_lines),
FadeIn(self.distance_labels),
FadeIn(self.sum_expression),
FadeIn(self.numbers),
ellipse.set_stroke, {"width": 0.5},
)
self.add(*self.focal_sum_things_to_add)
self.play(
FadeInFromDown(Q_label),
GrowFromCenter(Q_dot)
)
self.add_foreground_mobjects(Q_dot)
self.add(Q_label_animation)
self.play(
Q_dot.move_to, line.point_from_proportion(0.05),
rate_func=there_and_back,
run_time=4
)
self.wait()
# Show similar triangles
self.play(
FadeIn(triangles[0]),
ShowCreation(QP_line),
Animation(elbow),
)
self.add(QP_line_update)
for i in range(3):
self.play(
FadeIn(triangles[(i + 1) % 2]),
FadeOut(triangles[i % 2]),
Animation(self.distance_labels),
Animation(elbow)
)
self.play(
FadeOut(triangles[1]),
Animation(self.distance_labels)
)
# Move first distance label
# (boy, this got messy...hopefully no one ever has
# to read this.)
angle = QP_line.get_angle() - QE_line.get_angle()
Q_point = Q_dot.get_center()
for x in range(2):
self.play(ShowCreationThenDestruction(QE_line))
distance_label_copy = self.distance_labels[0].copy()
self.play(
ApplyFunction(
distance_label_rotate_update,
distance_label_copy,
path_arc=angle
),
Rotate(QE_line, angle, about_point=Q_point)
)
self.play(FadeOut(QE_line))
self.remove(distance_label_copy)
self.add(distance_label_rotate_update_animation)
self.focal_sum_things_to_add.append(
distance_label_rotate_update_animation
)
self.wait()
self.play(
Q_dot.move_to, line.point_from_proportion(0),
run_time=4,
rate_func=there_and_back
)
# Trace out ellipse
self.play(ShowCreation(radial_line))
self.wait()
self.play(
ApplyFunction(put_dot_at_intersection, Q_dot),
run_time=3,
)
self.wait()
self.play(
Rotating(
P_dot,
about_point=circle.get_center(),
rate_func=bezier([0, 0, 1, 1]),
run_time=10,
),
ellipse.restore,
*P_dot_movement_updates
)
self.wait()
# Talk through tangency
self.play(
ShowCreation(sum_rect),
Write(tangency_comment),
)
points = [line.get_end(), line.get_start(), Q_dot.get_center()]
run_times = [1, 3, 2]
for point, run_time in zip(points, run_times):
self.play(Q_dot.move_to, point, run_time=run_time)
self.wait()
self.remove(*self.focal_sum_things_to_add)
self.play(*map(FadeOut, [
radial_line,
QP_line,
P_dot, P_label,
Q_dot, Q_label,
elbow,
self.distance_labels,
self.numbers,
self.sum_expression,
sum_rect,
tangency_comment,
]))
self.wait()
# Show all lines
lines.remove(line)
ghost_lines.remove(ghost_line)
for line in lines:
line.generate_target()
line.target.rotate(90 * DEGREES)
self.play(
LaggedStart(FadeIn, ghost_lines),
LaggedStart(FadeIn, lines),
)
self.play(LaggedStart(MoveToTarget, lines))
self.wait()
def show_orbiting_planet(self):
ellipse = self.ellipse
ep_dot = self.ep_dot
planet = ImageMobject("earth")
planet.scale_to_fit_height(0.25)
orbit = Orbiting(planet, ep_dot, ellipse)
lines = self.lines
def update_lines(lines):
for gl, line in zip(self.ghost_lines, lines):
intersection = line_intersection(
[self.circle.get_center(), gl.get_end()],
line.get_start_and_end()
)
distance = np.linalg.norm(
intersection - planet.get_center()
)
if distance < 0.025:
line.set_color(BLUE)
self.add(line)
else:
line.set_color(WHITE)
lines_update_animation = ContinualUpdateFromFunc(
lines, update_lines
)
self.add(orbit)
self.add(lines_update_animation)
self.add_foreground_mobjects(planet)
self.wait(12)
class EndOfGeometryProofiness(Scene):
def construct(self):
pass