Up to AskAboutEllipses in lost_lecture project

This commit is contained in:
Grant Sanderson
2018-07-12 15:27:36 -07:00
parent d1e7cd3c56
commit aa6194cbbf

View File

@ -13,6 +13,8 @@ class Orbiting(ContinualAnimation):
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):
@ -30,6 +32,32 @@ class Orbiting(ContinualAnimation):
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
)
# Animations
@ -37,7 +65,7 @@ class ShowEmergingEllipse(Scene):
CONFIG = {
"circle_radius": 3,
"circle_color": BLUE,
"num_lines": 100,
"num_lines": 150,
"lines_stroke_width": 1,
"eccentricity_vector": 2 * RIGHT,
"ghost_lines_stroke_color": LIGHT_GREY,
@ -128,7 +156,7 @@ class ShowEmergingEllipse(Scene):
self.wait()
self.play(
ShowCreation(ellipse),
Write(ellipse_words, run_time=1)
FadeInFromDown(ellipse_words)
)
self.wait()
@ -408,12 +436,361 @@ class FeynmanLecturesScreenCaptureFrame(Scene):
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):
pass
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):
pass
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,
)
)
# 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 = TextMobject(
"""
I am going to give what I will call an
\\emph{elementary} demonstration. But elementary
does not mean easy to understand. Elementary
means that [nothing] very little is requried
to know ahead of time in order to understand it,
except to have an infinite amount of intelligence.
""",
tex_to_color_map={
"\\emph{elementary}": BLUE,
"elementary": BLUE,
"Elementary": BLUE,
}
)
self.add(quote)