mirror of
https://github.com/3b1b/manim.git
synced 2025-08-02 19:46:21 +08:00
Up to AskAboutEllipses in lost_lecture project
This commit is contained in:
@ -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)
|
||||
|
||||
|
Reference in New Issue
Block a user