mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Beginning KeplersSecondLaw scene
This commit is contained in:
parent
ab90c42ce1
commit
60a6b0aaa3
1 changed files with 250 additions and 28 deletions
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
from big_ol_pile_of_manim_imports import *
|
from big_ol_pile_of_manim_imports import *
|
||||||
|
|
||||||
|
COBALT = "#0047AB"
|
||||||
|
|
||||||
class Orbiting(ContinualAnimation):
|
class Orbiting(ContinualAnimation):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -576,7 +577,12 @@ class TheMotionOfPlanets(Scene):
|
||||||
class AskAboutEllipses(TheMotionOfPlanets):
|
class AskAboutEllipses(TheMotionOfPlanets):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"camera_config": {"background_opacity": 1},
|
"camera_config": {"background_opacity": 1},
|
||||||
|
"sun_center": ORIGIN,
|
||||||
"animate_sun": True,
|
"animate_sun": True,
|
||||||
|
"a": 3.5,
|
||||||
|
"b": 2.0,
|
||||||
|
"ellipse_color": WHITE,
|
||||||
|
"ellipse_stroke_width": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
def construct(self):
|
def construct(self):
|
||||||
|
@ -595,6 +601,7 @@ class AskAboutEllipses(TheMotionOfPlanets):
|
||||||
|
|
||||||
def add_sun(self):
|
def add_sun(self):
|
||||||
sun = ImageMobject("sun", height=0.5)
|
sun = ImageMobject("sun", height=0.5)
|
||||||
|
sun.move_to(self.sun_center)
|
||||||
self.sun = sun
|
self.sun = sun
|
||||||
self.add(sun)
|
self.add(sun)
|
||||||
if self.animate_sun:
|
if self.animate_sun:
|
||||||
|
@ -792,12 +799,15 @@ class AskAboutEllipses(TheMotionOfPlanets):
|
||||||
|
|
||||||
# Helpers
|
# Helpers
|
||||||
def get_ellipse(self):
|
def get_ellipse(self):
|
||||||
a = 7.0
|
a = self.a
|
||||||
b = 4.0
|
b = self.b
|
||||||
c = np.sqrt(a**2 - b**2)
|
c = np.sqrt(a**2 - b**2)
|
||||||
ellipse = Circle(radius=a / 2)
|
ellipse = Circle(radius=a)
|
||||||
ellipse.set_stroke(WHITE, 1)
|
ellipse.set_stroke(
|
||||||
ellipse.stretch(b / a, dim=1)
|
self.ellipse_color,
|
||||||
|
self.ellipse_stroke_width,
|
||||||
|
)
|
||||||
|
ellipse.stretch(fdiv(b, a), dim=1)
|
||||||
ellipse.move_to(
|
ellipse.move_to(
|
||||||
self.sun.get_center() + c * LEFT / 2
|
self.sun.get_center() + c * LEFT / 2
|
||||||
)
|
)
|
||||||
|
@ -1141,6 +1151,9 @@ class ShowEllipseDefiningProperty(Scene):
|
||||||
new_decimal.scale_to_fit_width(max_width)
|
new_decimal.scale_to_fit_width(max_width)
|
||||||
new_decimal.next_to(line, UP, SMALL_BUFF)
|
new_decimal.next_to(line, UP, SMALL_BUFF)
|
||||||
new_decimal.set_color(color)
|
new_decimal.set_color(color)
|
||||||
|
new_decimal.add_to_back(
|
||||||
|
new_decimal.copy().set_stroke(BLACK, 5)
|
||||||
|
)
|
||||||
VGroup(new_decimal, line).rotate(
|
VGroup(new_decimal, line).rotate(
|
||||||
-angle, about_point=ORIGIN
|
-angle, about_point=ORIGIN
|
||||||
)
|
)
|
||||||
|
@ -1187,25 +1200,19 @@ class ShowEllipseDefiningProperty(Scene):
|
||||||
|
|
||||||
|
|
||||||
class GeometryProofLand(Scene):
|
class GeometryProofLand(Scene):
|
||||||
def construct(self):
|
CONFIG = {
|
||||||
word = TextMobject("Geometry proof land")
|
"colors": [
|
||||||
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,
|
PINK, RED, YELLOW, GREEN, GREEN_A, BLUE,
|
||||||
MAROON_E, MAROON_B, YELLOW, BLUE,
|
MAROON_E, MAROON_B, YELLOW, BLUE,
|
||||||
]
|
],
|
||||||
word.set_color_by_gradient(*colors)
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
word = self.get_geometry_proof_land_word()
|
||||||
word_outlines = word.deepcopy()
|
word_outlines = word.deepcopy()
|
||||||
word_outlines.set_fill(opacity=0)
|
word_outlines.set_fill(opacity=0)
|
||||||
word_outlines.set_stroke(WHITE, 1)
|
word_outlines.set_stroke(WHITE, 1)
|
||||||
|
colors = list(self.colors)
|
||||||
random.shuffle(colors)
|
random.shuffle(colors)
|
||||||
word_outlines.set_color_by_gradient(*colors)
|
word_outlines.set_color_by_gradient(*colors)
|
||||||
|
|
||||||
|
@ -1230,6 +1237,19 @@ class GeometryProofLand(Scene):
|
||||||
))
|
))
|
||||||
self.wait()
|
self.wait()
|
||||||
|
|
||||||
|
def get_geometry_proof_land_word(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)
|
||||||
|
word.set_color_by_gradient(*self.colors)
|
||||||
|
return word
|
||||||
|
|
||||||
|
|
||||||
class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -1374,10 +1394,10 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
self.add(*to_add)
|
self.add(*to_add)
|
||||||
|
|
||||||
points = [
|
points = [
|
||||||
ellipse.get_bottom(),
|
|
||||||
circle.point_from_proportion(0.9),
|
|
||||||
ellipse.get_top(),
|
ellipse.get_top(),
|
||||||
ellipse.point_from_proportion(0.7),
|
circle.point_from_proportion(0.2),
|
||||||
|
ellipse.point_from_proportion(0.2),
|
||||||
|
ellipse.point_from_proportion(0.4),
|
||||||
]
|
]
|
||||||
for point in points:
|
for point in points:
|
||||||
self.play(
|
self.play(
|
||||||
|
@ -1555,7 +1575,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
self.get_eccentricity_point()
|
self.get_eccentricity_point()
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
triangles.set_color_by_gradient(PINK, GREEN)
|
triangles.set_color_by_gradient(RED_C, COBALT)
|
||||||
triangles.set_stroke(WHITE, 2)
|
triangles.set_stroke(WHITE, 2)
|
||||||
|
|
||||||
# Add even more distant label updates
|
# Add even more distant label updates
|
||||||
|
@ -1609,7 +1629,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
)
|
)
|
||||||
elbow_update_animation = UpdateFromFunc(
|
elbow_update_animation = UpdateFromFunc(
|
||||||
elbow,
|
elbow,
|
||||||
lambda e: Transform(e, self.get_elbow(line)).update(1)
|
lambda e: Transform(e, self.get_elbow(ghost_line)).update(1)
|
||||||
)
|
)
|
||||||
|
|
||||||
P_dot_movement_updates = [
|
P_dot_movement_updates = [
|
||||||
|
@ -1638,7 +1658,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
|
|
||||||
# Why is this needed?!?
|
# Why is this needed?!?
|
||||||
self.add(*self.focal_sum_things_to_add)
|
self.add(*self.focal_sum_things_to_add)
|
||||||
self.wait(0)
|
self.wait(0.01)
|
||||||
self.remove(*self.focal_sum_things_to_add)
|
self.remove(*self.focal_sum_things_to_add)
|
||||||
|
|
||||||
# Show label
|
# Show label
|
||||||
|
@ -1659,6 +1679,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
FadeInFromDown(Q_label),
|
FadeInFromDown(Q_label),
|
||||||
GrowFromCenter(Q_dot)
|
GrowFromCenter(Q_dot)
|
||||||
)
|
)
|
||||||
|
self.wait()
|
||||||
self.add_foreground_mobjects(Q_dot)
|
self.add_foreground_mobjects(Q_dot)
|
||||||
self.add(Q_label_animation)
|
self.add(Q_label_animation)
|
||||||
self.play(
|
self.play(
|
||||||
|
@ -1794,10 +1815,10 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
intersection - planet.get_center()
|
intersection - planet.get_center()
|
||||||
)
|
)
|
||||||
if distance < 0.025:
|
if distance < 0.025:
|
||||||
line.set_color(BLUE)
|
line.set_stroke(BLUE, 3)
|
||||||
self.add(line)
|
self.add(line)
|
||||||
else:
|
else:
|
||||||
line.set_color(WHITE)
|
line.set_stroke(WHITE, 1)
|
||||||
|
|
||||||
lines_update_animation = ContinualUpdateFromFunc(
|
lines_update_animation = ContinualUpdateFromFunc(
|
||||||
lines, update_lines
|
lines, update_lines
|
||||||
|
@ -1809,6 +1830,207 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
|
||||||
self.wait(12)
|
self.wait(12)
|
||||||
|
|
||||||
|
|
||||||
class EndOfGeometryProofiness(Scene):
|
class EndOfGeometryProofiness(GeometryProofLand):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
geometry_word = self.get_geometry_proof_land_word()
|
||||||
|
orbital_mechanics = TextMobject("Orbital Mechanics")
|
||||||
|
orbital_mechanics.scale(1.5)
|
||||||
|
orbital_mechanics.to_edge(UP)
|
||||||
|
underline = Line(LEFT, RIGHT)
|
||||||
|
underline.match_width(orbital_mechanics)
|
||||||
|
underline.next_to(orbital_mechanics, DOWN, SMALL_BUFF)
|
||||||
|
|
||||||
|
self.play(LaggedStart(FadeOutAndShiftDown, geometry_word))
|
||||||
|
self.play(FadeInFromDown(orbital_mechanics))
|
||||||
|
self.play(ShowCreation(underline))
|
||||||
|
self.wait()
|
||||||
|
|
||||||
|
|
||||||
|
class KeplersSecondLaw(AskAboutEllipses):
|
||||||
|
CONFIG = {
|
||||||
|
"sun_center": 3 * RIGHT + DOWN,
|
||||||
|
"animate_sun": False,
|
||||||
|
"a": 5.0,
|
||||||
|
"b": 2.5,
|
||||||
|
"ellipse_stroke_width": 2,
|
||||||
|
"area_color": COBALT,
|
||||||
|
"area_opacity": 0.75,
|
||||||
|
"arc_color": YELLOW,
|
||||||
|
"arc_stroke_width": 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
def construct(self):
|
||||||
|
self.add_title()
|
||||||
|
self.add_sun()
|
||||||
|
self.add_foreground_mobjects(self.sun)
|
||||||
|
self.add_orbit()
|
||||||
|
self.add_foreground_mobjects(self.comet)
|
||||||
|
|
||||||
|
self.show_several_sweeps()
|
||||||
|
self.contrast_close_to_far()
|
||||||
|
|
||||||
|
|
||||||
|
def add_title(self):
|
||||||
|
title = TextMobject("Kepler's 2nd law")
|
||||||
|
title.scale(1.0)
|
||||||
|
title.to_edge(UP)
|
||||||
|
self.add(title)
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
def show_several_sweeps(self):
|
||||||
|
n_sweeps = 3
|
||||||
|
shown_areas = VGroup()
|
||||||
|
for x in range(n_sweeps):
|
||||||
|
self.wait()
|
||||||
|
area = self.show_area_sweep()
|
||||||
|
shown_areas.add(area)
|
||||||
|
self.wait(2)
|
||||||
|
self.play(FadeOut(shown_areas))
|
||||||
|
|
||||||
|
|
||||||
|
def contrast_close_to_far(self):
|
||||||
|
orbit = self.orbit
|
||||||
|
sun_point = self.sun.get_center()
|
||||||
|
|
||||||
|
start_prop = 0.8
|
||||||
|
self.wait_until_proportion(start_prop)
|
||||||
|
area = self.show_area_sweep()
|
||||||
|
end_prop = max(0.9, orbit.proportion)
|
||||||
|
arc = self.get_arc(start_prop, end_prop)
|
||||||
|
radius = Line(sun_point, arc.points[0])
|
||||||
|
radius.set_color(PINK)
|
||||||
|
|
||||||
|
|
||||||
|
radius_words = self.get_radius_words(
|
||||||
|
radius, "Short"
|
||||||
|
)
|
||||||
|
|
||||||
|
arc_words = TextMobject("Long arc")
|
||||||
|
angle = 9 * DEGREES
|
||||||
|
arc_words.rotate(angle)
|
||||||
|
arc_words.scale(0.1)
|
||||||
|
vect = rotate_vector(RIGHT, angle)
|
||||||
|
arc_words.next_to(vect, vect)
|
||||||
|
arc_words.apply_complex_function(np.exp)
|
||||||
|
arc_words.scale(2)
|
||||||
|
arc_words.next_to(
|
||||||
|
arc.point_from_proportion(0.5),
|
||||||
|
rotate_vector(vect, 90 * DEGREES),
|
||||||
|
buff=-MED_SMALL_BUFF,
|
||||||
|
)
|
||||||
|
arc_words.match_color(arc)
|
||||||
|
|
||||||
|
# Show stubby arc
|
||||||
|
# self.remove(orbit)
|
||||||
|
# self.add(self.comet)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(radius),
|
||||||
|
Write(radius_words),
|
||||||
|
)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(arc),
|
||||||
|
Write(arc_words)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Show narrow arc
|
||||||
|
# (Code repetition...uck)
|
||||||
|
start_prop = 0.4
|
||||||
|
self.wait_until_proportion(start_prop)
|
||||||
|
area = self.show_area_sweep()
|
||||||
|
end_prop = max(0.45, orbit.proportion)
|
||||||
|
arc = self.get_arc(start_prop, end_prop)
|
||||||
|
radius = Line(sun_point, arc.points[0])
|
||||||
|
radius.set_color(PINK)
|
||||||
|
radius_words = self.get_radius_words(radius, "Long")
|
||||||
|
|
||||||
|
# Helpers
|
||||||
|
def show_area_sweep(self, time=1.0):
|
||||||
|
orbit = self.orbit
|
||||||
|
start_prop = orbit.proportion
|
||||||
|
area = self.get_area(start_prop, start_prop)
|
||||||
|
area_update = UpdateFromFunc(
|
||||||
|
area,
|
||||||
|
lambda a: Transform(
|
||||||
|
a, self.get_area(start_prop, orbit.proportion)
|
||||||
|
).update(1)
|
||||||
|
)
|
||||||
|
|
||||||
|
self.play(area_update, run_time=time)
|
||||||
|
|
||||||
|
return area
|
||||||
|
|
||||||
|
def get_area(self, prop1, prop2):
|
||||||
|
"""
|
||||||
|
Return a mobject illustrating the area swept
|
||||||
|
out between a point prop1 of the way along
|
||||||
|
the ellipse, and prop2 of the way.
|
||||||
|
"""
|
||||||
|
sun_point = self.sun.get_center()
|
||||||
|
arc = self.get_arc(prop1, prop2)
|
||||||
|
|
||||||
|
# Add lines from start
|
||||||
|
result = VMobject()
|
||||||
|
result.append_vectorized_mobject(
|
||||||
|
Line(sun_point, arc.points[0])
|
||||||
|
)
|
||||||
|
result.append_vectorized_mobject(arc)
|
||||||
|
result.append_vectorized_mobject(
|
||||||
|
Line(arc.points[-1], sun_point)
|
||||||
|
)
|
||||||
|
|
||||||
|
result.set_stroke(WHITE, width=0)
|
||||||
|
result.set_fill(
|
||||||
|
self.area_color,
|
||||||
|
self.area_opacity,
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def get_arc(self, prop1, prop2):
|
||||||
|
sun_point = self.sun.get_center()
|
||||||
|
ellipse = self.get_ellipse()
|
||||||
|
prop1 = prop1 % 1.0
|
||||||
|
prop2 = prop2 % 1.0
|
||||||
|
|
||||||
|
if prop2 > prop1:
|
||||||
|
arc = VMobject().pointwise_become_partial(
|
||||||
|
ellipse, prop1, prop2
|
||||||
|
)
|
||||||
|
elif prop1 > prop2:
|
||||||
|
arc, arc_extension = [
|
||||||
|
VMobject().pointwise_become_partial(
|
||||||
|
ellipse, p1, p2
|
||||||
|
)
|
||||||
|
for p1, p2 in [(prop1, 1.0), (0.0, prop2)]
|
||||||
|
]
|
||||||
|
arc.append_vectorized_mobject(arc_extension)
|
||||||
|
else:
|
||||||
|
arc = VectorizedPoint(
|
||||||
|
ellipse.point_from_proportion(prop1)
|
||||||
|
)
|
||||||
|
|
||||||
|
arc.set_stroke(
|
||||||
|
self.arc_color,
|
||||||
|
self.arc_stroke_width,
|
||||||
|
)
|
||||||
|
|
||||||
|
return arc
|
||||||
|
|
||||||
|
def wait_until_proportion(self, prop):
|
||||||
|
if self.skip_animations:
|
||||||
|
self.orbit.proportion = prop
|
||||||
|
else:
|
||||||
|
while self.orbit.proportion < prop:
|
||||||
|
self.wait(0.2)
|
||||||
|
|
||||||
|
def get_radius_words(self, radius, adjective):
|
||||||
|
radius_words = TextMobject(
|
||||||
|
"%s radius" % adjective,
|
||||||
|
)
|
||||||
|
radius_words.scale_to_fit_width(
|
||||||
|
0.8 * radius.get_length()
|
||||||
|
)
|
||||||
|
radius_words.match_color(radius)
|
||||||
|
radius_words.next_to(ORIGIN, DOWN, SMALL_BUFF)
|
||||||
|
radius_words.rotate(radius.get_angle(), about_point=ORIGIN)
|
||||||
|
radius_words.shift(radius.get_center())
|
||||||
|
return radius_words
|
||||||
|
|
Loading…
Add table
Reference in a new issue