Beginning KeplersSecondLaw scene

This commit is contained in:
Grant Sanderson
2018-07-16 12:37:45 -07:00
parent ab90c42ce1
commit 60a6b0aaa3

View File

@ -1,6 +1,7 @@
from __future__ import absolute_import
from big_ol_pile_of_manim_imports import *
COBALT = "#0047AB"
class Orbiting(ContinualAnimation):
CONFIG = {
@ -576,7 +577,12 @@ class TheMotionOfPlanets(Scene):
class AskAboutEllipses(TheMotionOfPlanets):
CONFIG = {
"camera_config": {"background_opacity": 1},
"sun_center": ORIGIN,
"animate_sun": True,
"a": 3.5,
"b": 2.0,
"ellipse_color": WHITE,
"ellipse_stroke_width": 1,
}
def construct(self):
@ -595,6 +601,7 @@ class AskAboutEllipses(TheMotionOfPlanets):
def add_sun(self):
sun = ImageMobject("sun", height=0.5)
sun.move_to(self.sun_center)
self.sun = sun
self.add(sun)
if self.animate_sun:
@ -792,12 +799,15 @@ class AskAboutEllipses(TheMotionOfPlanets):
# Helpers
def get_ellipse(self):
a = 7.0
b = 4.0
a = self.a
b = self.b
c = np.sqrt(a**2 - b**2)
ellipse = Circle(radius=a / 2)
ellipse.set_stroke(WHITE, 1)
ellipse.stretch(b / a, dim=1)
ellipse = Circle(radius=a)
ellipse.set_stroke(
self.ellipse_color,
self.ellipse_stroke_width,
)
ellipse.stretch(fdiv(b, a), dim=1)
ellipse.move_to(
self.sun.get_center() + c * LEFT / 2
)
@ -1141,6 +1151,9 @@ class ShowEllipseDefiningProperty(Scene):
new_decimal.scale_to_fit_width(max_width)
new_decimal.next_to(line, UP, SMALL_BUFF)
new_decimal.set_color(color)
new_decimal.add_to_back(
new_decimal.copy().set_stroke(BLACK, 5)
)
VGroup(new_decimal, line).rotate(
-angle, about_point=ORIGIN
)
@ -1187,25 +1200,19 @@ class ShowEllipseDefiningProperty(Scene):
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 = [
CONFIG = {
"colors": [
PINK, RED, YELLOW, GREEN, GREEN_A, 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.set_fill(opacity=0)
word_outlines.set_stroke(WHITE, 1)
colors = list(self.colors)
random.shuffle(colors)
word_outlines.set_color_by_gradient(*colors)
@ -1230,6 +1237,19 @@ class GeometryProofLand(Scene):
))
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):
CONFIG = {
@ -1374,10 +1394,10 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
self.add(*to_add)
points = [
ellipse.get_bottom(),
circle.point_from_proportion(0.9),
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:
self.play(
@ -1555,7 +1575,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
self.get_eccentricity_point()
]
])
triangles.set_color_by_gradient(PINK, GREEN)
triangles.set_color_by_gradient(RED_C, COBALT)
triangles.set_stroke(WHITE, 2)
# Add even more distant label updates
@ -1609,7 +1629,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
)
elbow_update_animation = UpdateFromFunc(
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 = [
@ -1638,7 +1658,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
# Why is this needed?!?
self.add(*self.focal_sum_things_to_add)
self.wait(0)
self.wait(0.01)
self.remove(*self.focal_sum_things_to_add)
# Show label
@ -1659,6 +1679,7 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
FadeInFromDown(Q_label),
GrowFromCenter(Q_dot)
)
self.wait()
self.add_foreground_mobjects(Q_dot)
self.add(Q_label_animation)
self.play(
@ -1794,10 +1815,10 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
intersection - planet.get_center()
)
if distance < 0.025:
line.set_color(BLUE)
line.set_stroke(BLUE, 3)
self.add(line)
else:
line.set_color(WHITE)
line.set_stroke(WHITE, 1)
lines_update_animation = ContinualUpdateFromFunc(
lines, update_lines
@ -1809,6 +1830,207 @@ class ProveEllipse(ShowEmergingEllipse, ShowEllipseDefiningProperty):
self.wait(12)
class EndOfGeometryProofiness(Scene):
class EndOfGeometryProofiness(GeometryProofLand):
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