Files
manim/active_projects/lost_lecture.py
2018-07-14 10:32:39 -07:00

1815 lines
56 KiB
Python

from __future__ import absolute_import
from big_ol_pile_of_manim_imports import *
class Orbiting(ContinualAnimation):
CONFIG = {
"rate": 0.3,
}
def __init__(self, planet, star, ellipse, **kwargs):
self.planet = planet
self.star = star
self.ellipse = ellipse
# Proportion of the way around the ellipse
self.proportion = 0
planet.move_to(ellipse.point_from_proportion(0))
ContinualAnimation.__init__(self, planet, **kwargs)
def update_mobject(self, dt):
# time = self.internal_time
rate = self.rate
planet = self.planet
star = self.star
ellipse = self.ellipse
rate *= 1 / np.linalg.norm(
planet.get_center() - star.get_center()
)
self.proportion += rate * dt
self.proportion = self.proportion % 1
planet.move_to(ellipse.point_from_proportion(self.proportion))
class SunAnimation(ContinualAnimation):
CONFIG = {
"rate": 0.2,
"angle": 60 * DEGREES,
}
def __init__(self, sun, **kwargs):
self.sun = sun
self.rotated_sun = sun.deepcopy()
self.rotated_sun.rotate(60 * DEGREES)
ContinualAnimation.__init__(
self, Group(sun, self.rotated_sun), **kwargs
)
def update_mobject(self, dt):
time = self.internal_time
a = (np.sin(self.rate * time * TAU) + 1) / 2.0
self.rotated_sun.rotate(-self.angle)
self.rotated_sun.move_to(self.sun)
self.rotated_sun.rotate(self.angle)
self.rotated_sun.pixel_array = np.array(
a * self.sun.pixel_array,
dtype=self.sun.pixel_array.dtype
)
class ShowWord(Animation):
CONFIG = {
"time_per_char": 0.06,
"rate_func": None,
}
def __init__(self, word, **kwargs):
assert(isinstance(word, SingleStringTexMobject))
digest_config(self, kwargs)
run_time = kwargs.pop(
"run_time",
self.time_per_char * len(word)
)
self.stroke_width = word.get_stroke_width()
Animation.__init__(self, word, run_time=run_time, **kwargs)
def update_mobject(self, alpha):
word = self.mobject
stroke_width = self.stroke_width
count = int(alpha * len(word))
remainder = (alpha * len(word)) % 1
word[:count].set_fill(opacity=1)
word[:count].set_stroke(width=stroke_width)
if count < len(word):
word[count].set_fill(opacity=remainder)
word[count].set_stroke(width=remainder * stroke_width)
word[count + 1:].set_fill(opacity=0)
word[count + 1:].set_stroke(width=0)
# Animations
class ShowEmergingEllipse(Scene):
CONFIG = {
"circle_radius": 3,
"circle_color": BLUE,
"num_lines": 150,
"lines_stroke_width": 1,
"eccentricity_vector": 2 * RIGHT,
"ghost_lines_stroke_color": LIGHT_GREY,
"ghost_lines_stroke_width": 0.5,
"ellipse_color": PINK,
}
def construct(self):
circle = self.get_circle()
e_point = self.get_eccentricity_point()
e_dot = Dot(e_point, color=YELLOW)
lines = self.get_lines()
ellipse = self.get_ellipse()
fade_rect = FullScreenFadeRectangle()
line = lines[len(lines) / 5]
line_dot = Dot(line.get_center(), color=YELLOW)
line_dot.scale(0.5)
ghost_line = self.get_ghost_lines(line)
ghost_lines = self.get_ghost_lines(lines)
rot_words = TextMobject("Rotate $90^\\circ$ \\\\ about center")
rot_words.next_to(line_dot, RIGHT)
elbow = self.get_elbow(line)
eccentric_words = TextMobject("``Eccentric'' point")
eccentric_words.next_to(circle.get_center(), DOWN)
ellipse_words = TextMobject("Perfect ellipse")
ellipse_words.next_to(ellipse, UP, SMALL_BUFF)
for text in rot_words, ellipse_words:
text.add_to_back(text.copy().set_stroke(BLACK, 5))
shuffled_lines = VGroup(*lines)
random.shuffle(shuffled_lines.submobjects)
self.play(ShowCreation(circle))
self.play(
FadeInAndShiftFromDirection(e_dot, LEFT),
Write(eccentric_words, run_time=1)
)
self.wait()
self.play(
LaggedStart(ShowCreation, shuffled_lines),
Animation(VGroup(e_dot, circle)),
FadeOut(eccentric_words)
)
self.add(ghost_lines)
self.add(e_dot, circle)
self.wait()
self.play(
FadeIn(fade_rect),
Animation(line),
GrowFromCenter(line_dot),
FadeInFromDown(rot_words),
)
self.wait()
self.add(ghost_line)
self.play(
MoveToTarget(line, path_arc=90 * DEGREES),
Animation(rot_words),
ShowCreation(elbow)
)
self.wait()
self.play(
FadeOut(fade_rect),
FadeOut(line_dot),
FadeOut(rot_words),
FadeOut(elbow),
Animation(line),
Animation(ghost_line)
)
self.play(
LaggedStart(MoveToTarget, lines, run_time=4),
Animation(VGroup(e_dot, circle))
)
self.wait()
self.play(
ShowCreation(ellipse),
FadeInFromDown(ellipse_words)
)
self.wait()
def get_circle(self):
circle = self.circle = Circle(
radius=self.circle_radius,
color=self.circle_color
)
return circle
def get_eccentricity_point(self):
return self.circle.get_center() + self.eccentricity_vector
def get_lines(self):
center = self.circle.get_center()
radius = self.circle.get_width() / 2
e_point = self.get_eccentricity_point()
lines = VGroup(*[
Line(
e_point,
center + rotate_vector(radius * RIGHT, angle)
)
for angle in np.linspace(0, TAU, self.num_lines)
])
lines.set_stroke(width=self.lines_stroke_width)
for line in lines:
line.generate_target()
line.target.rotate(90 * DEGREES)
return lines
def get_ghost_lines(self, lines):
return lines.copy().set_stroke(
color=self.ghost_lines_stroke_color,
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()
radius = self.circle.get_width() / 2
# Ellipse parameters
a = radius / 2
c = np.linalg.norm(e_point - center) / 2
b = np.sqrt(a**2 - c**2)
result = Circle(radius=b, color=self.ellipse_color)
result.stretch(a / b, 0)
result.move_to(Line(center, e_point))
return result
class FeynmanAndOrbitingPlannetOnEllipseDiagram(ShowEmergingEllipse):
def construct(self):
circle = self.get_circle()
lines = self.get_lines()
ghost_lines = self.get_ghost_lines(lines)
for line in lines:
MoveToTarget(line).update(1)
ellipse = self.get_ellipse()
e_dot = Dot(self.get_eccentricity_point())
e_dot.set_color(YELLOW)
comet = ImageMobject("earth")
comet.scale_to_fit_width(0.3)
feynman = ImageMobject("Feynman")
feynman.scale_to_fit_height(6)
feynman.next_to(ORIGIN, LEFT)
feynman.to_edge(UP)
feynman_name = TextMobject("Richard Feynman")
feynman_name.next_to(feynman, DOWN)
feynman.save_state()
feynman.shift(2 * DOWN)
feynman_rect = BackgroundRectangle(
feynman, fill_opacity=1
)
group = VGroup(circle, ghost_lines, lines, e_dot, ellipse)
self.add(group)
self.add(Orbiting(comet, e_dot, ellipse))
self.add_foreground_mobjects(comet)
self.wait()
self.play(
feynman.restore,
MaintainPositionRelativeTo(feynman_rect, feynman),
VFadeOut(feynman_rect),
group.to_edge, RIGHT,
)
self.play(Write(feynman_name))
self.wait()
self.wait(10)
class FeynmanFame(Scene):
def construct(self):
books = VGroup(
ImageMobject("Feynman_QED_cover"),
ImageMobject("Surely_Youre_Joking_cover"),
ImageMobject("Feynman_Lectures_cover"),
)
for book in books:
book.scale_to_fit_height(6)
book.move_to(FRAME_WIDTH * LEFT / 4)
feynman_diagram = self.get_feynman_diagram()
feynman_diagram.next_to(ORIGIN, RIGHT)
fd_parts = VGroup(*reversed(feynman_diagram.family_members_with_points()))
# As a physicist
self.play(self.get_book_intro(books[0]))
self.play(LaggedStart(
Write, feynman_diagram,
run_time=4
))
self.wait()
self.play(
self.get_book_intro(books[1]),
self.get_book_outro(books[0]),
LaggedStart(
ApplyMethod, fd_parts,
lambda m: (m.scale, 0),
run_time=1
),
)
self.remove(feynman_diagram)
self.wait()
# As a public figure
safe = SVGMobject(file_name="safe", height=2)
safe_rect = SurroundingRectangle(safe, buff=0)
safe_rect.set_stroke(width=0)
safe_rect.set_fill(DARK_GREY, 1)
safe.add_to_back(safe_rect)
bongo = SVGMobject(file_name="bongo")
bongo.scale_to_fit_height(1)
bongo.set_color(WHITE)
bongo.next_to(safe, RIGHT, LARGE_BUFF)
objects = VGroup(safe, bongo)
feynman_smile = ImageMobject("Feynman_Los_Alamos")
feynman_smile.scale_to_fit_height(4)
feynman_smile.next_to(objects, DOWN)
VGroup(objects, feynman_smile).next_to(ORIGIN, RIGHT)
joke = TextMobject(
"``Science is the belief \\\\ in the ignorance of \\\\ experts.''"
)
joke.move_to(objects)
self.play(LaggedStart(
DrawBorderThenFill, objects,
lag_ratio=0.75
))
self.play(self.get_book_intro(feynman_smile))
self.wait()
self.play(
objects.shift, 2 * UP,
VFadeOut(objects)
)
self.play(Write(joke))
self.wait(2)
self.play(
self.get_book_intro(books[2]),
self.get_book_outro(books[1]),
LaggedStart(FadeOut, joke, run_time=1),
ApplyMethod(
feynman_smile.shift, FRAME_HEIGHT * DOWN,
remover=True
)
)
# As a teacher
feynman_teacher = ImageMobject("Feynman_teaching")
feynman_teacher.scale_to_fit_width(FRAME_WIDTH / 2 - 1)
feynman_teacher.next_to(ORIGIN, RIGHT)
self.play(self.get_book_intro(feynman_teacher))
self.wait(3)
def get_book_animation(self, book,
initial_shift,
animated_shift,
opacity_func
):
rect = BackgroundRectangle(book, fill_opacity=1)
book.shift(initial_shift)
return AnimationGroup(
ApplyMethod(book.shift, animated_shift),
UpdateFromAlphaFunc(
rect, lambda r, a: r.move_to(book).set_fill(
opacity=opacity_func(a)
),
remover=True
)
)
def get_book_intro(self, book):
return self.get_book_animation(
book, 2 * DOWN, 2 * UP, lambda a: 1 - a
)
def get_book_outro(self, book):
return ApplyMethod(book.shift, FRAME_HEIGHT * UP, remover=True)
def get_feynman_diagram(self):
x_min = -1.5
x_max = 1.5
arrow = Arrow(LEFT, RIGHT, buff=0, use_rectangular_stem=False)
arrow.tip.move_to(arrow.get_center())
arrows = VGroup(*[
arrow.copy().rotate(angle).next_to(point, vect, buff=0)
for (angle, point, vect) in [
(-45 * DEGREES, x_min * RIGHT, UL),
(-135 * DEGREES, x_min * RIGHT, DL),
(-135 * DEGREES, x_max * RIGHT, UR),
(-45 * DEGREES, x_max * RIGHT, DR),
]
])
labels = VGroup(*[
TexMobject(tex)
for tex in ["e^-", "e^+", "\\text{\\=q}", "q"]
])
vects = [UR, DR, UL, DL]
for arrow, label, vect in zip(arrows, labels, vects):
label.next_to(arrow.get_center(), vect, buff=SMALL_BUFF)
wave = FunctionGraph(
lambda x: 0.2 * np.sin(2 * TAU * x),
x_min=x_min,
x_max=x_max,
)
wave_label = TexMobject("\\gamma")
wave_label.next_to(wave, UP, SMALL_BUFF)
labels.add(wave_label)
squiggle = ParametricFunction(
lambda t: np.array([
t + 0.5 * np.sin(TAU * t),
0.5 * np.cos(TAU * t),
0,
]),
t_min=0,
t_max=4,
)
squiggle.scale(0.25)
squiggle.set_color(BLUE)
squiggle.rotate(-30 * DEGREES)
squiggle.next_to(
arrows[2].point_from_proportion(0.75),
DR, buff=0
)
squiggle_label = TexMobject("g")
squiggle_label.next_to(squiggle, UR, buff=-MED_SMALL_BUFF)
labels.add(squiggle_label)
return VGroup(arrows, wave, squiggle, labels)
class FeynmanLecturesScreenCaptureFrame(Scene):
def construct(self):
url = TextMobject("http://www.feynmanlectures.caltech.edu/")
url.to_edge(UP)
screen_rect = ScreenRectangle(height=6)
screen_rect.next_to(url, DOWN)
self.add(url)
self.play(ShowCreation(screen_rect))
self.wait()
class TheMotionOfPlanets(Scene):
CONFIG = {
"camera_config": {"background_opacity": 1},
"random_seed": 2,
}
def construct(self):
self.add_title()
self.setup_orbits()
def add_title(self):
title = TextMobject("``The motion of planets around the sun''")
title.set_color(YELLOW)
title.to_edge(UP)
title.add_to_back(title.copy().set_stroke(BLACK, 5))
self.add(title)
self.title = title
def setup_orbits(self):
sun = ImageMobject("sun")
sun.scale_to_fit_height(0.7)
planets, ellipses, orbits = self.get_planets_ellipses_and_orbits(sun)
archivist_words = TextMobject(
"Judith Goodstein (Caltech archivist)"
)
archivist_words.to_corner(UL)
archivist_words.shift(1.5 * DOWN)
archivist_words.add_background_rectangle()
alt_name = TextMobject("David Goodstein (Caltech physicist)")
alt_name.next_to(archivist_words, DOWN, aligned_edge=LEFT)
alt_name.add_background_rectangle()
book = ImageMobject("Lost_Lecture_cover")
book.scale_to_fit_height(4)
book.next_to(alt_name, DOWN)
self.add(SunAnimation(sun))
self.add(ellipses, planets)
self.add(self.title)
self.add(*orbits)
self.add_foreground_mobjects(planets)
self.wait(10)
self.play(
VGroup(ellipses, sun).shift, 3 * RIGHT,
FadeInFromDown(archivist_words),
Animation(self.title)
)
self.add_foreground_mobjects(archivist_words)
self.wait(3)
self.play(FadeInFromDown(alt_name))
self.add_foreground_mobjects(alt_name)
self.wait()
self.play(FadeInFromDown(book))
self.wait(15)
def get_planets_ellipses_and_orbits(self, sun):
planets = VGroup(
ImageMobject("mercury"),
ImageMobject("venus"),
ImageMobject("earth"),
ImageMobject("mars"),
ImageMobject("comet")
)
sizes = [0.383, 0.95, 1.0, 0.532, 0.3]
orbit_radii = [0.254, 0.475, 0.656, 1.0, 3.0]
orbit_eccentricies = [0.206, 0.006, 0.0167, 0.0934, 0.967]
for planet, size in zip(planets, sizes):
planet.scale_to_fit_height(0.5)
planet.scale(size)
ellipses = VGroup(*[
Circle(radius=r, color=WHITE, stroke_width=1)
for r in orbit_radii
])
for circle, ec in zip(ellipses, orbit_eccentricies):
a = circle.get_height() / 2
c = ec * a
b = np.sqrt(a**2 - c**2)
circle.stretch(b / a, 1)
c = np.sqrt(a**2 - b**2)
circle.shift(c * RIGHT)
for circle in ellipses:
circle.rotate(
TAU * np.random.random(),
about_point=ORIGIN
)
ellipses.scale(3.5, about_point=ORIGIN)
orbits = [
Orbiting(
planet, sun, circle,
rate=0.25 * r**(2 / 3)
)
for planet, circle, r in zip(planets, ellipses, orbit_radii)
]
orbits[-1].proportion = 0.15
orbits[-1].rate = 0.5
return planets, ellipses, orbits
class AskAboutEllipses(TheMotionOfPlanets):
CONFIG = {
"camera_config": {"background_opacity": 1},
"animate_sun": True,
}
def construct(self):
self.add_title()
self.add_sun()
self.add_orbit()
self.add_focus_lines()
self.add_force_labels()
self.comment_on_imperfections()
self.set_up_differential_equations()
def add_title(self):
title = Title("Why are orbits ellipses?")
self.add(title)
self.title = title
def add_sun(self):
sun = ImageMobject("sun", height=0.5)
self.sun = sun
self.add(sun)
if self.animate_sun:
self.add(SunAnimation(sun))
def add_orbit(self):
sun = self.sun
comet = ImageMobject("comet")
comet.scale_to_fit_height(0.2)
ellipse = self.get_ellipse()
orbit = Orbiting(comet, sun, ellipse)
self.add(ellipse)
self.add(orbit)
self.ellipse = ellipse
self.comet = comet
self.orbit = orbit
def add_focus_lines(self):
f1, f2 = self.focus_points
comet = self.comet
lines = VGroup(Line(LEFT, RIGHT), Line(LEFT, RIGHT))
lines.set_stroke(LIGHT_GREY, 1)
def update_lines(lines):
l1, l2 = lines
P = comet.get_center()
l1.put_start_and_end_on(f1, P)
l2.put_start_and_end_on(f2, P)
return lines
animation = ContinualUpdateFromFunc(
lines, update_lines
)
self.add(animation)
self.wait(8)
self.focus_lines = lines
self.focus_lines_animation = animation
def add_force_labels(self):
radial_line = self.focus_lines[0]
# Radial line measurement
radius_measurement_kwargs = {
"num_decimal_places": 3,
"color": BLUE,
}
radius_measurement = DecimalNumber(1, **radius_measurement_kwargs)
def update_radial_measurement(measurement):
angle = -radial_line.get_angle() + np.pi
radial_line.rotate(angle, about_point=ORIGIN)
new_decimal = DecimalNumber(
radial_line.get_length(),
**radius_measurement_kwargs
)
max_width = 0.6 * radial_line.get_width()
if new_decimal.get_width() > max_width:
new_decimal.scale_to_fit_width(max_width)
new_decimal.next_to(radial_line, UP, SMALL_BUFF)
VGroup(new_decimal, radial_line).rotate(
-angle, about_point=ORIGIN
)
Transform(measurement, new_decimal).update(1)
radius_measurement_animation = ContinualUpdateFromFunc(
radius_measurement, update_radial_measurement
)
# Force equation
force_equation = TexMobject(
"F = {GMm \\over (0.000)^2}",
tex_to_color_map={
"F": YELLOW,
"0.000": BLACK,
}
)
force_equation.next_to(self.title, DOWN)
force_equation.to_edge(RIGHT)
radius_in_denominator_ref = force_equation.get_part_by_tex("0.000")
radius_in_denominator = DecimalNumber(
0, **radius_measurement_kwargs
)
radius_in_denominator.scale(0.95)
update_radius_in_denominator = ContinualChangingDecimal(
radius_in_denominator,
lambda a: radial_line.get_length(),
position_update_func=lambda mob: mob.move_to(
radius_in_denominator_ref, LEFT
)
)
# Force arrow
force_arrow = Arrow(LEFT, RIGHT, color=YELLOW)
def update_force_arrow(arrow):
radius = radial_line.get_length()
# target_length = 1 / radius**2
target_length = 1 / radius # Lies!
arrow.scale(
target_length / arrow.get_length()
)
arrow.rotate(
np.pi + radial_line.get_angle() - arrow.get_angle()
)
arrow.shift(
radial_line.get_end() - arrow.get_start()
)
force_arrow_animation = ContinualUpdateFromFunc(
force_arrow, update_force_arrow
)
inverse_square_law_words = TextMobject(
"``Inverse square law''"
)
inverse_square_law_words.next_to(force_equation, DOWN, MED_LARGE_BUFF)
inverse_square_law_words.to_edge(RIGHT)
force_equation.next_to(inverse_square_law_words, UP, MED_LARGE_BUFF)
def v_fade_in(mobject):
return UpdateFromAlphaFunc(
mobject,
lambda mob, alpha: mob.set_fill(opacity=alpha)
)
self.add(update_radius_in_denominator)
self.add(radius_measurement_animation)
self.play(
FadeIn(force_equation),
v_fade_in(radius_in_denominator),
v_fade_in(radius_measurement)
)
self.add(force_arrow_animation)
self.play(v_fade_in(force_arrow))
self.wait(8)
self.play(Write(inverse_square_law_words))
self.wait(9)
self.force_equation = force_equation
self.inverse_square_law_words = inverse_square_law_words
self.force_arrow = force_arrow
self.radius_measurement = radius_measurement
def comment_on_imperfections(self):
planets, ellipses, orbits = self.get_planets_ellipses_and_orbits(self.sun)
orbits.pop(-1)
ellipses.submobjects.pop(-1)
planets.submobjects.pop(-1)
scale_factor = 20
center = self.sun.get_center()
ellipses.save_state()
ellipses.scale(scale_factor, about_point=center)
self.add(*orbits)
self.play(ellipses.restore, Animation(planets))
self.wait(7)
self.play(
ellipses.scale, scale_factor, {"about_point": center},
Animation(planets)
)
self.remove(*orbits)
self.remove(planets, ellipses)
self.wait(2)
def set_up_differential_equations(self):
d_dt = TexMobject("{d \\over dt}")
in_vect = Matrix(np.array([
"x(t)",
"y(t)",
"\\dot{x}(t)",
"\\dot{y}(t)",
]))
equals = TexMobject("=")
out_vect = Matrix(np.array([
"\\dot{x}(t)",
"\\dot{y}(t)",
"-x(t) / (x(t)^2 + y(t)^2)^{3/2}",
"-y(t) / (x(t)^2 + y(t)^2)^{3/2}",
]), element_alignment_corner=ORIGIN)
equation = VGroup(d_dt, in_vect, equals, out_vect)
equation.arrange_submobjects(RIGHT, buff=SMALL_BUFF)
equation.scale_to_fit_width(6)
equation.to_corner(DR, buff=MED_LARGE_BUFF)
cross = Cross(equation)
self.play(Write(equation))
self.wait(6)
self.play(ShowCreation(cross))
self.wait(6)
# Helpers
def get_ellipse(self):
a = 7.0
b = 4.0
c = np.sqrt(a**2 - b**2)
ellipse = Circle(radius=a / 2)
ellipse.set_stroke(WHITE, 1)
ellipse.stretch(b / a, dim=1)
ellipse.move_to(
self.sun.get_center() + c * LEFT / 2
)
self.focus_points = [
self.sun.get_center(),
self.sun.get_center() + c * LEFT,
]
return ellipse
class FeynmanElementaryQuote(Scene):
def construct(self):
quote_text = """
\\large
I am going to give what I will call an
\\emph{elementary} demonstration. But elementary
does not mean easy to understand. Elementary
means that very little is required
to know ahead of time in order to understand it,
except to have an infinite amount of intelligence.
"""
quote_parts = filter(lambda s: s, quote_text.split(" "))
quote = TextMobject(
*quote_parts,
tex_to_color_map={
"\\emph{elementary}": BLUE,
"elementary": BLUE,
"Elementary": BLUE,
"infinite": YELLOW,
"amount": YELLOW,
"of": YELLOW,
"intelligence": YELLOW,
"very": RED,
"little": RED,
},
alignment=""
)
quote[-1].shift(2 * SMALL_BUFF * LEFT)
quote.scale_to_fit_width(FRAME_WIDTH - 1)
quote.to_edge(UP)
quote.get_part_by_tex("of").set_color(WHITE)
nothing = TextMobject("nothing")
nothing.scale(0.9)
very = quote.get_part_by_tex("very")
nothing.shift(very[0].get_left() - nothing[0].get_left())
nothing.set_color(RED)
for word in quote:
if word is very:
self.add_foreground_mobjects(nothing)
self.play(ShowWord(nothing))
self.wait(0.2)
nothing.sort_submobjects(lambda p: -p[0])
self.play(LaggedStart(
FadeOut, nothing,
run_time=1
))
self.remove_foreground_mobject(nothing)
back_word = word.copy().set_stroke(BLACK, 5)
self.add_foreground_mobjects(back_word, word)
self.play(
ShowWord(back_word),
ShowWord(word),
)
self.wait(0.005 * len(word)**1.5)
class LostLecturePicture(TODOStub):
CONFIG = {"camera_config": {"background_opacity": 1}}
def construct(self):
picture = ImageMobject("Feynman_teaching")
picture.scale_to_fit_height(FRAME_WIDTH)
picture.to_corner(UL, buff=0)
picture.fade(0.5)
self.play(
picture.to_corner, DR, {"buff": 0},
picture.shift, 1.5 * DOWN,
path_arc=60 * DEGREES,
run_time=20,
rate_func=bezier([0, 0, 1, 1])
)
class AskAboutInfiniteIntelligence(TeacherStudentsScene):
def construct(self):
self.student_says(
"Infinite intelligence?",
target_mode="confused"
)
self.play(
self.get_student_changes("horrified", "confused", "sad"),
self.teacher.change, "happy",
)
self.wait()
self.teacher_says(
"It's not too bad, \\\\ but stay focused",
added_anims=[self.get_student_changes(*["happy"] * 3)]
)
self.wait()
self.look_at(self.screen)
self.wait(5)
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.label_foci()
self.label_focal_sum()
def add_ellipse(self):
a = self.a
b = self.b
ellipse = Circle(radius=a, color=self.ellipse_color)
ellipse.stretch(fdiv(b, a), dim=1)
ellipse.to_edge(LEFT, buff=LARGE_BUFF)
self.ellipse = ellipse
self.add(ellipse)
def add_focal_lines(self):
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):
lines = self.focal_lines
colors = [YELLOW, PINK]
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):
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