2018-01-17 15:18:02 -08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
from helpers import *
|
|
|
|
|
|
|
|
from mobject.tex_mobject import TexMobject
|
|
|
|
from mobject import Mobject
|
|
|
|
from mobject.image_mobject import ImageMobject
|
|
|
|
from mobject.vectorized_mobject import *
|
|
|
|
|
|
|
|
from animation.animation import Animation
|
|
|
|
from animation.transform import *
|
|
|
|
from animation.simple_animations import *
|
2018-01-21 18:29:13 -08:00
|
|
|
from animation.continual_animation import *
|
|
|
|
|
2018-01-17 15:18:02 -08:00
|
|
|
from animation.playground import *
|
|
|
|
from topics.geometry import *
|
|
|
|
from topics.characters import *
|
|
|
|
from topics.functions import *
|
|
|
|
from topics.number_line import *
|
|
|
|
from topics.numerals import *
|
2018-02-06 11:12:50 +01:00
|
|
|
#from topics.combinatorics import *
|
2018-01-17 15:18:02 -08:00
|
|
|
from scene import Scene
|
|
|
|
from camera import Camera
|
|
|
|
from mobject.svg_mobject import *
|
|
|
|
from mobject.tex_mobject import *
|
2018-02-06 11:12:50 +01:00
|
|
|
from topics.three_dimensions import *
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
from topics.light import *
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
LIGHT_COLOR = YELLOW
|
|
|
|
INDICATOR_RADIUS = 0.7
|
|
|
|
INDICATOR_STROKE_WIDTH = 1
|
2018-01-21 18:29:13 -08:00
|
|
|
INDICATOR_STROKE_COLOR = WHITE
|
|
|
|
INDICATOR_TEXT_COLOR = WHITE
|
|
|
|
INDICATOR_UPDATE_TIME = 0.2
|
2018-01-22 14:41:45 -08:00
|
|
|
FAST_INDICATOR_UPDATE_TIME = 0.1
|
2018-01-18 15:06:38 -08:00
|
|
|
OPACITY_FOR_UNIT_INTENSITY = 0.2
|
2018-02-06 11:12:50 +01:00
|
|
|
SWITCH_ON_RUN_TIME = 1.5
|
2018-01-22 14:41:45 -08:00
|
|
|
FAST_SWITCH_ON_RUN_TIME = 0.1
|
2018-01-27 13:30:25 +01:00
|
|
|
NUM_LEVELS = 30
|
2018-02-06 11:12:50 +01:00
|
|
|
NUM_CONES = 7 # in first lighthouse scene
|
2018-01-23 10:53:24 -08:00
|
|
|
NUM_VISIBLE_CONES = 5 # ibidem
|
|
|
|
ARC_TIP_LENGTH = 0.2
|
2018-02-06 11:12:50 +01:00
|
|
|
AMBIENT_FULL = 0.5
|
2018-01-27 15:17:31 +01:00
|
|
|
AMBIENT_DIMMED = 0.2
|
2018-02-06 11:12:50 +01:00
|
|
|
SPOTLIGHT_FULL = 0.9
|
|
|
|
SPOTLIGHT_DIMMED = 0.2
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
LIGHT_COLOR = YELLOW
|
2018-01-27 13:30:25 +01:00
|
|
|
DEGREES = TAU/360
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
inverse_power_law = lambda maxint,scale,cutoff,exponent: \
|
|
|
|
(lambda r: maxint * (cutoff/(r/scale+cutoff))**exponent)
|
|
|
|
inverse_quadratic = lambda maxint,scale,cutoff: inverse_power_law(maxint,scale,cutoff,2)
|
2018-01-23 10:53:24 -08:00
|
|
|
|
|
|
|
|
|
|
|
class AngleUpdater(ContinualAnimation):
|
2018-02-06 11:12:50 +01:00
|
|
|
def __init__(self, angle_arc, spotlight, **kwargs):
|
2018-01-23 10:53:24 -08:00
|
|
|
self.angle_arc = angle_arc
|
|
|
|
self.source_point = angle_arc.get_arc_center()
|
2018-02-06 11:12:50 +01:00
|
|
|
self.spotlight = spotlight
|
2018-01-23 10:53:24 -08:00
|
|
|
ContinualAnimation.__init__(self, self.angle_arc, **kwargs)
|
|
|
|
|
|
|
|
def update_mobject(self, dt):
|
|
|
|
new_arc = self.angle_arc.copy().set_bound_angles(
|
2018-02-06 11:12:50 +01:00
|
|
|
start = self.spotlight.start_angle(),
|
|
|
|
stop = self.spotlight.stop_angle()
|
2018-01-23 10:53:24 -08:00
|
|
|
)
|
|
|
|
new_arc.generate_points()
|
|
|
|
new_arc.move_arc_center_to(self.source_point)
|
|
|
|
self.angle_arc.points = new_arc.points
|
2018-02-06 11:12:50 +01:00
|
|
|
self.angle_arc.add_tip(tip_length = ARC_TIP_LENGTH,
|
|
|
|
at_start = True, at_end = True)
|
2018-01-18 15:06:38 -08:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-25 23:23:04 +01:00
|
|
|
|
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
class LightIndicator(Mobject):
|
|
|
|
CONFIG = {
|
|
|
|
"radius": 0.5,
|
2018-01-21 18:29:13 -08:00
|
|
|
"intensity": 0,
|
2018-01-27 19:09:29 +01:00
|
|
|
"opacity_for_unit_intensity": 1,
|
2018-02-06 11:12:50 +01:00
|
|
|
"precision": 3,
|
|
|
|
"show_reading": True
|
2018-01-18 15:06:38 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
def generate_points(self):
|
|
|
|
self.background = Circle(color=BLACK, radius = self.radius)
|
|
|
|
self.background.set_fill(opacity=1)
|
|
|
|
self.foreground = Circle(color=self.color, radius = self.radius)
|
|
|
|
self.foreground.set_stroke(color=INDICATOR_STROKE_COLOR,width=INDICATOR_STROKE_WIDTH)
|
|
|
|
|
|
|
|
self.add(self.background, self.foreground)
|
2018-01-27 19:09:29 +01:00
|
|
|
self.reading = DecimalNumber(self.intensity,num_decimal_points = self.precision)
|
2018-01-18 15:06:38 -08:00
|
|
|
self.reading.set_fill(color=INDICATOR_TEXT_COLOR)
|
|
|
|
self.reading.move_to(self.get_center())
|
2018-02-06 11:12:50 +01:00
|
|
|
if self.show_reading:
|
|
|
|
self.add(self.reading)
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
def set_intensity(self, new_int):
|
|
|
|
self.intensity = new_int
|
|
|
|
new_opacity = min(1, new_int * self.opacity_for_unit_intensity)
|
|
|
|
self.foreground.set_fill(opacity=new_opacity)
|
|
|
|
ChangeDecimalToValue(self.reading, new_int).update(1)
|
2018-02-06 11:12:50 +01:00
|
|
|
return self
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UpdateLightIndicator(AnimationGroup):
|
|
|
|
|
|
|
|
def __init__(self, indicator, intensity, **kwargs):
|
|
|
|
if not isinstance(indicator,LightIndicator):
|
|
|
|
raise Exception("This transform applies only to LightIndicator")
|
|
|
|
|
|
|
|
target_foreground = indicator.copy().set_intensity(intensity).foreground
|
|
|
|
change_opacity = Transform(
|
|
|
|
indicator.foreground, target_foreground
|
|
|
|
)
|
|
|
|
changing_decimal = ChangeDecimalToValue(indicator.reading, intensity)
|
|
|
|
AnimationGroup.__init__(self, changing_decimal, change_opacity, **kwargs)
|
|
|
|
self.mobject = indicator
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 15:18:02 -08:00
|
|
|
class IntroScene(PiCreatureScene):
|
|
|
|
|
|
|
|
CONFIG = {
|
|
|
|
"rect_height" : 0.2,
|
|
|
|
"duration" : 1.0,
|
|
|
|
"eq_spacing" : 3 * MED_LARGE_BUFF
|
|
|
|
}
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
randy = self.get_primary_pi_creature()
|
|
|
|
randy.scale(0.7).to_corner(DOWN+RIGHT)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
self.build_up_euler_sum()
|
2018-01-17 15:18:02 -08:00
|
|
|
self.build_up_sum_on_number_line()
|
|
|
|
self.show_pi_answer()
|
|
|
|
self.other_pi_formulas()
|
2018-01-18 15:06:38 -08:00
|
|
|
self.refocus_on_euler_sum()
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_up_euler_sum(self):
|
|
|
|
|
|
|
|
self.euler_sum = TexMobject(
|
|
|
|
"1", "+",
|
|
|
|
"{1 \\over 4}", "+",
|
|
|
|
"{1 \\over 9}", "+",
|
|
|
|
"{1 \\over 16}", "+",
|
|
|
|
"{1 \\over 25}", "+",
|
|
|
|
"\\cdots", "=",
|
|
|
|
arg_separator = " \\, "
|
|
|
|
)
|
|
|
|
|
|
|
|
self.euler_sum.to_edge(UP)
|
|
|
|
self.euler_sum.shift(2*LEFT)
|
|
|
|
|
|
|
|
terms = [1./n**2 for n in range(1,6)]
|
|
|
|
partial_results_values = np.cumsum(terms)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(self.euler_sum[0], run_time = self.duration)
|
|
|
|
)
|
|
|
|
|
|
|
|
equals_sign = self.euler_sum.get_part_by_tex("=")
|
|
|
|
|
|
|
|
self.partial_sum_decimal = DecimalNumber(partial_results_values[1],
|
|
|
|
num_decimal_points = 2)
|
|
|
|
self.partial_sum_decimal.next_to(equals_sign, RIGHT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for i in range(4):
|
|
|
|
|
|
|
|
FadeIn(self.partial_sum_decimal, run_time = self.duration)
|
|
|
|
|
|
|
|
if i == 0:
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(self.euler_sum[1], run_time = self.duration),
|
|
|
|
FadeIn(self.euler_sum[2], run_time = self.duration),
|
|
|
|
FadeIn(equals_sign, run_time = self.duration),
|
|
|
|
FadeIn(self.partial_sum_decimal, run_time = self.duration)
|
|
|
|
)
|
|
|
|
|
|
|
|
else:
|
|
|
|
self.play(
|
|
|
|
FadeIn(self.euler_sum[2*i+1], run_time = self.duration),
|
|
|
|
FadeIn(self.euler_sum[2*i+2], run_time = self.duration),
|
|
|
|
ChangeDecimalToValue(
|
|
|
|
self.partial_sum_decimal,
|
|
|
|
partial_results_values[i+1],
|
|
|
|
run_time = self.duration,
|
|
|
|
num_decimal_points = 6,
|
|
|
|
show_ellipsis = True,
|
|
|
|
position_update_func = lambda m: m.next_to(equals_sign, RIGHT)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
self.q_marks = TextMobject("???").highlight(LIGHT_COLOR)
|
2018-01-17 15:18:02 -08:00
|
|
|
self.q_marks.move_to(self.partial_sum_decimal)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(self.euler_sum[-3], run_time = self.duration), # +
|
|
|
|
FadeIn(self.euler_sum[-2], run_time = self.duration), # ...
|
|
|
|
ReplacementTransform(self.partial_sum_decimal, self.q_marks)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def build_up_sum_on_number_line(self):
|
|
|
|
|
|
|
|
self.number_line = NumberLine(
|
|
|
|
x_min = 0,
|
|
|
|
color = WHITE,
|
|
|
|
number_at_center = 1,
|
|
|
|
stroke_width = 1,
|
|
|
|
numbers_with_elongated_ticks = [0,1,2,3],
|
|
|
|
numbers_to_show = np.arange(0,5),
|
|
|
|
unit_size = 5,
|
|
|
|
tick_frequency = 0.2,
|
|
|
|
line_to_number_buff = MED_LARGE_BUFF
|
|
|
|
)
|
|
|
|
|
|
|
|
self.number_line_labels = self.number_line.get_number_mobjects()
|
|
|
|
self.add(self.number_line,self.number_line_labels)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# create slabs for series terms
|
|
|
|
|
|
|
|
max_n = 10
|
|
|
|
|
|
|
|
terms = [0] + [1./(n**2) for n in range(1, max_n + 1)]
|
|
|
|
series_terms = np.cumsum(terms)
|
|
|
|
lines = VGroup()
|
|
|
|
self.rects = VGroup()
|
|
|
|
slab_colors = [YELLOW, BLUE] * (max_n / 2)
|
|
|
|
|
|
|
|
for t1, t2, color in zip(series_terms, series_terms[1:], slab_colors):
|
|
|
|
line = Line(*map(self.number_line.number_to_point, [t1, t2]))
|
|
|
|
rect = Rectangle()
|
|
|
|
rect.stroke_width = 0
|
|
|
|
rect.fill_opacity = 1
|
|
|
|
rect.highlight(color)
|
|
|
|
rect.stretch_to_fit_height(
|
|
|
|
self.rect_height,
|
|
|
|
)
|
|
|
|
rect.stretch_to_fit_width(line.get_width())
|
|
|
|
rect.move_to(line)
|
|
|
|
|
|
|
|
self.rects.add(rect)
|
|
|
|
lines.add(line)
|
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
#self.rects.radial_gradient_highlight(ORIGIN, 5, YELLOW, BLUE)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
for i in range(5):
|
|
|
|
self.play(
|
|
|
|
GrowFromPoint(self.rects[i], self.euler_sum[2*i].get_center(),
|
|
|
|
run_time = self.duration)
|
|
|
|
)
|
|
|
|
|
|
|
|
for i in range(5, max_n):
|
|
|
|
self.play(
|
|
|
|
GrowFromPoint(self.rects[i], self.euler_sum[10].get_center(),
|
|
|
|
run_time = self.duration)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def show_pi_answer(self):
|
|
|
|
|
|
|
|
self.pi_answer = TexMobject("{\\pi^2 \\over 6}").highlight(YELLOW)
|
|
|
|
self.pi_answer.move_to(self.partial_sum_decimal)
|
|
|
|
self.pi_answer.next_to(self.euler_sum[-1], RIGHT,
|
|
|
|
submobject_to_align = self.pi_answer[-2])
|
|
|
|
self.play(ReplacementTransform(self.q_marks, self.pi_answer))
|
|
|
|
|
|
|
|
|
|
|
|
def other_pi_formulas(self):
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(self.rects),
|
|
|
|
FadeOut(self.number_line_labels),
|
|
|
|
FadeOut(self.number_line)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.leibniz_sum = TexMobject(
|
|
|
|
"1-{1\\over 3}+{1\\over 5}-{1\\over 7}+{1\\over 9}-\\cdots",
|
|
|
|
"=", "{\\pi \\over 4}")
|
|
|
|
|
|
|
|
self.wallis_product = TexMobject(
|
|
|
|
"{2\\over 1} \\cdot {2\\over 3} \\cdot {4\\over 3} \\cdot {4\\over 5}" +
|
|
|
|
"\\cdot {6\\over 5} \\cdot {6\\over 7} \\cdots",
|
|
|
|
"=", "{\\pi \\over 2}")
|
|
|
|
|
|
|
|
self.leibniz_sum.next_to(self.euler_sum.get_part_by_tex("="), DOWN,
|
|
|
|
buff = self.eq_spacing,
|
|
|
|
submobject_to_align = self.leibniz_sum.get_part_by_tex("=")
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wallis_product.next_to(self.leibniz_sum.get_part_by_tex("="), DOWN,
|
|
|
|
buff = self.eq_spacing,
|
|
|
|
submobject_to_align = self.wallis_product.get_part_by_tex("=")
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
Write(self.leibniz_sum)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Write(self.wallis_product)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def refocus_on_euler_sum(self):
|
|
|
|
|
|
|
|
self.euler_sum.add(self.pi_answer)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(self.leibniz_sum),
|
|
|
|
FadeOut(self.wallis_product),
|
|
|
|
ApplyMethod(self.euler_sum.shift,
|
|
|
|
ORIGIN + 2*UP - self.euler_sum.get_center())
|
|
|
|
)
|
|
|
|
|
|
|
|
# focus on pi squared
|
|
|
|
pi_squared = self.euler_sum.get_part_by_tex("\\pi")[-3]
|
|
|
|
self.play(
|
|
|
|
ScaleInPlace(pi_squared,2,rate_func = wiggle)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Morty thinks of a circle
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
q_circle = Circle(
|
|
|
|
stroke_color = YELLOW,
|
|
|
|
fill_color = YELLOW,
|
|
|
|
fill_opacity = 0.5,
|
|
|
|
radius = 0.4,
|
|
|
|
stroke_width = 10.0
|
|
|
|
)
|
|
|
|
q_mark = TexMobject("?")
|
|
|
|
q_mark.next_to(q_circle)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
thought = Group(q_circle, q_mark)
|
|
|
|
q_mark.scale_to_fit_height(0.8 * q_circle.get_height())
|
|
|
|
self.pi_creature_thinks(thought,target_mode = "confused",
|
|
|
|
bubble_kwargs = { "height" : 2, "width" : 3 })
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FirstLighthouseScene(PiCreatureScene):
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
def construct(self):
|
2018-01-21 18:29:13 -08:00
|
|
|
self.remove(self.get_primary_pi_creature())
|
2018-01-17 15:18:02 -08:00
|
|
|
self.show_lighthouses_on_number_line()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_lighthouses_on_number_line(self):
|
|
|
|
|
|
|
|
self.number_line = NumberLine(
|
|
|
|
x_min = 0,
|
|
|
|
color = WHITE,
|
2018-01-21 18:29:13 -08:00
|
|
|
number_at_center = 1.6,
|
2018-01-17 15:18:02 -08:00
|
|
|
stroke_width = 1,
|
2018-01-22 14:41:45 -08:00
|
|
|
numbers_with_elongated_ticks = range(1,5),
|
|
|
|
numbers_to_show = range(1,5),
|
2018-01-18 15:06:38 -08:00
|
|
|
unit_size = 2,
|
2018-01-17 15:18:02 -08:00
|
|
|
tick_frequency = 0.2,
|
2018-01-22 14:41:45 -08:00
|
|
|
line_to_number_buff = LARGE_BUFF,
|
|
|
|
label_direction = UP,
|
2018-01-17 15:18:02 -08:00
|
|
|
)
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
self.number_line.label_direction = DOWN
|
|
|
|
|
2018-01-17 15:18:02 -08:00
|
|
|
self.number_line_labels = self.number_line.get_number_mobjects()
|
|
|
|
self.add(self.number_line,self.number_line_labels)
|
|
|
|
self.wait()
|
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
origin_point = self.number_line.number_to_point(0)
|
|
|
|
|
|
|
|
self.default_pi_creature_class = Randolph
|
|
|
|
randy = self.get_primary_pi_creature()
|
|
|
|
|
|
|
|
randy.scale(0.5)
|
|
|
|
randy.flip()
|
|
|
|
right_pupil = randy.pupils[1]
|
|
|
|
randy.next_to(origin_point, LEFT, buff = 0, submobject_to_align = right_pupil)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
light_indicator = LightIndicator(radius = INDICATOR_RADIUS,
|
2018-01-21 18:29:13 -08:00
|
|
|
opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
|
|
|
|
color = LIGHT_COLOR)
|
|
|
|
light_indicator.reading.scale(0.8)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
bubble = ThoughtBubble(direction = RIGHT,
|
|
|
|
width = 2.5, height = 3.5)
|
|
|
|
bubble.next_to(randy,LEFT+UP)
|
|
|
|
bubble.add_content(light_indicator)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
randy.change, "wave_2",
|
|
|
|
ShowCreation(bubble),
|
|
|
|
FadeIn(light_indicator)
|
|
|
|
)
|
2018-01-18 15:06:38 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
light_sources = []
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
euler_sum_above = TexMobject("1", "+", "{1\over 4}",
|
2018-01-23 10:53:24 -08:00
|
|
|
"+", "{1\over 9}", "+", "{1\over 16}", "+", "{1\over 25}", "+", "{1\over 36}")
|
2018-01-22 14:41:45 -08:00
|
|
|
|
|
|
|
for (i,term) in zip(range(len(euler_sum_above)),euler_sum_above):
|
|
|
|
#horizontal alignment with tick marks
|
|
|
|
term.next_to(self.number_line.number_to_point(0.5*i+1),UP,buff = 2)
|
|
|
|
# vertical alignment with light indicator
|
|
|
|
old_y = term.get_center()[1]
|
|
|
|
new_y = light_indicator.get_center()[1]
|
|
|
|
term.shift([0,new_y - old_y,0])
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
|
|
|
|
|
|
|
|
for i in range(1,NUM_CONES+1):
|
2018-02-06 11:12:50 +01:00
|
|
|
light_source = LightSource(
|
2018-01-27 13:30:25 +01:00
|
|
|
opacity_function = inverse_quadratic(1,2,1),
|
|
|
|
num_levels = NUM_LEVELS,
|
2018-02-06 11:12:50 +01:00
|
|
|
radius = 12.0,
|
|
|
|
)
|
|
|
|
point = self.number_line.number_to_point(i)
|
|
|
|
light_source.move_source_to(point)
|
|
|
|
light_sources.append(light_source)
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
for ls in light_sources:
|
|
|
|
self.add_foreground_mobject(ls.lighthouse)
|
2018-01-18 15:06:38 -08:00
|
|
|
|
|
|
|
light_indicator.set_intensity(0)
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
intensities = np.cumsum(np.array([1./n**2 for n in range(1,NUM_CONES+1)]))
|
2018-01-18 15:06:38 -08:00
|
|
|
opacities = intensities * light_indicator.opacity_for_unit_intensity
|
|
|
|
|
|
|
|
self.remove_foreground_mobjects(light_indicator)
|
|
|
|
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
# slowly switch on visible light cones and increment indicator
|
2018-02-06 11:12:50 +01:00
|
|
|
for (i,light_source) in zip(range(NUM_VISIBLE_CONES),light_sources[:NUM_VISIBLE_CONES]):
|
|
|
|
indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/light_source.radius * self.number_line.unit_size
|
2018-01-21 18:29:13 -08:00
|
|
|
indicator_stop_time = indicator_start_time + INDICATOR_UPDATE_TIME
|
2018-01-23 10:53:24 -08:00
|
|
|
indicator_rate_func = squish_rate_func(
|
2018-01-21 18:29:13 -08:00
|
|
|
smooth,indicator_start_time,indicator_stop_time)
|
2018-01-18 15:06:38 -08:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
SwitchOn(light_source.ambient_light),
|
2018-01-22 14:41:45 -08:00
|
|
|
FadeIn(euler_sum_above[2*i], run_time = SWITCH_ON_RUN_TIME,
|
|
|
|
rate_func = indicator_rate_func),
|
|
|
|
FadeIn(euler_sum_above[2*i - 1], run_time = SWITCH_ON_RUN_TIME,
|
|
|
|
rate_func = indicator_rate_func),
|
2018-02-06 11:12:50 +01:00
|
|
|
# this last line *technically* fades in the last term, but it is off-screen
|
2018-01-21 18:29:13 -08:00
|
|
|
ChangeDecimalToValue(light_indicator.reading,intensities[i],
|
|
|
|
rate_func = indicator_rate_func, run_time = SWITCH_ON_RUN_TIME),
|
2018-01-18 15:06:38 -08:00
|
|
|
ApplyMethod(light_indicator.foreground.set_fill,None,opacities[i])
|
|
|
|
)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
if i == 0:
|
2018-01-22 14:41:45 -08:00
|
|
|
# move a copy out of the thought bubble for comparison
|
2018-01-21 18:29:13 -08:00
|
|
|
light_indicator_copy = light_indicator.copy()
|
2018-01-22 14:41:45 -08:00
|
|
|
old_y = light_indicator_copy.get_center()[1]
|
|
|
|
new_y = self.number_line.get_center()[1]
|
2018-01-21 18:29:13 -08:00
|
|
|
self.play(
|
2018-01-22 14:41:45 -08:00
|
|
|
light_indicator_copy.shift,[0, new_y - old_y,0]
|
2018-01-21 18:29:13 -08:00
|
|
|
)
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
# quickly switch on off-screen light cones and increment indicator
|
2018-02-06 11:12:50 +01:00
|
|
|
for (i,light_source) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),light_sources[NUM_VISIBLE_CONES:NUM_CONES]):
|
|
|
|
indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/light_source.radius * self.number_line.unit_size
|
2018-01-22 14:41:45 -08:00
|
|
|
indicator_stop_time = indicator_start_time + FAST_INDICATOR_UPDATE_TIME
|
|
|
|
indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9)
|
|
|
|
smooth,indicator_start_time,indicator_stop_time)
|
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
SwitchOn(light_source.ambient_light, run_time = FAST_SWITCH_ON_RUN_TIME),
|
|
|
|
ChangeDecimalToValue(light_indicator.reading,intensities[i-1],
|
2018-01-22 14:41:45 -08:00
|
|
|
rate_func = indicator_rate_func, run_time = FAST_SWITCH_ON_RUN_TIME),
|
2018-02-06 11:12:50 +01:00
|
|
|
ApplyMethod(light_indicator.foreground.set_fill,None,opacities[i-1])
|
2018-01-22 14:41:45 -08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# show limit value in light indicator and an equals sign
|
|
|
|
limit_reading = TexMobject("{\pi^2 \over 6}")
|
|
|
|
limit_reading.move_to(light_indicator.reading)
|
2018-01-23 10:53:24 -08:00
|
|
|
|
|
|
|
equals_sign = TexMobject("=")
|
|
|
|
equals_sign.next_to(randy, UP)
|
|
|
|
old_y = equals_sign.get_center()[1]
|
|
|
|
new_y = euler_sum_above.get_center()[1]
|
|
|
|
equals_sign.shift([0,new_y - old_y,0])
|
|
|
|
|
2018-01-22 14:41:45 -08:00
|
|
|
self.play(
|
2018-01-23 10:53:24 -08:00
|
|
|
FadeOut(light_indicator.reading),
|
|
|
|
FadeIn(limit_reading),
|
|
|
|
FadeIn(equals_sign),
|
2018-01-22 14:41:45 -08:00
|
|
|
)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-01-18 15:06:38 -08:00
|
|
|
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SingleLighthouseScene(PiCreatureScene):
|
2018-01-21 18:29:13 -08:00
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.force_skipping()
|
2018-01-29 15:20:57 +01:00
|
|
|
self.setup_elements()
|
|
|
|
self.setup_trackers() # spotlight and angle msmt change when screen rotates
|
|
|
|
self.rotate_screen()
|
2018-02-06 11:12:50 +01:00
|
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.morph_lighthouse_into_sun()
|
2018-01-21 18:29:13 -08:00
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def setup_elements(self):
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.remove(self.get_primary_pi_creature())
|
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
SCREEN_SIZE = 3.0
|
|
|
|
DISTANCE_FROM_LIGHTHOUSE = 10.0
|
|
|
|
source_point = [-DISTANCE_FROM_LIGHTHOUSE/2,0,0]
|
|
|
|
observer_point = [DISTANCE_FROM_LIGHTHOUSE/2,0,0]
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
# Light source
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.light_source = LightSource(
|
|
|
|
opacity_function = inverse_quadratic(1,2,1),
|
2018-01-27 13:30:25 +01:00
|
|
|
num_levels = NUM_LEVELS,
|
2018-01-23 10:53:24 -08:00
|
|
|
radius = 10,
|
2018-02-06 11:12:50 +01:00
|
|
|
max_opacity_ambient = AMBIENT_FULL
|
2018-01-21 18:29:13 -08:00
|
|
|
)
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
self.light_source.move_source_to(source_point)
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
# Pi Creature
|
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
morty = self.get_primary_pi_creature()
|
|
|
|
morty.scale(0.5)
|
|
|
|
morty.move_to(observer_point)
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.add(self.light_source.lighthouse,morty)
|
2018-01-21 18:29:13 -08:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
SwitchOn(self.light_source.ambient_light)
|
2018-01-21 18:29:13 -08:00
|
|
|
)
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Screen
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.light_source.set_screen(Line([3,-1,0],[3,1,0]))
|
|
|
|
self.light_source.spotlight.screen.rotate(-TAU/6)
|
|
|
|
self.light_source.spotlight.screen.next_to(morty,LEFT)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
# Animations
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(FadeIn(self.light_source.spotlight.screen))
|
|
|
|
self.wait()
|
2018-01-23 10:53:24 -08:00
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
# just calling .dim_ambient via ApplyMethod does not work, why?
|
|
|
|
dimmed_ambient_light = self.light_source.ambient_light.copy()
|
|
|
|
dimmed_ambient_light.dimming(AMBIENT_DIMMED)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
Transform(self.light_source.ambient_light,dimmed_ambient_light),
|
|
|
|
FadeIn(self.light_source.spotlight)
|
2018-01-29 15:20:57 +01:00
|
|
|
)
|
2018-02-06 11:12:50 +01:00
|
|
|
self.add(self.light_source.spotlight.shadow)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
self.add_foreground_mobject(morty)
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup_trackers(self):
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Make spotlight follow the screen
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
#self.screen_tracker = MovingScreenTracker(self.screen, light_source = self.light_source)
|
|
|
|
#self.source_tracker = MovingSourceTracker(self.light_source)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
#self.add(self.screen_tracker, self.source_tracker)
|
|
|
|
self.screen_tracker = ScreenTracker(self.light_source.spotlight)
|
|
|
|
self.add(self.screen_tracker)
|
|
|
|
|
|
|
|
pointing_screen_at_source = Rotate(self.light_source.spotlight.screen,TAU/6)
|
2018-01-27 13:30:25 +01:00
|
|
|
self.play(pointing_screen_at_source)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(self.light_source.move_source_to,ORIGIN)
|
|
|
|
|
|
|
|
#self.play(self.spotlight.move_to,ORIGIN)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# angle msmt (arc)
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
arc_angle = self.light_source.spotlight.opening_angle()
|
2018-01-23 10:53:24 -08:00
|
|
|
# draw arc arrows to show the opening angle
|
2018-02-06 11:12:50 +01:00
|
|
|
self.angle_arc = Arc(radius = 5, start_angle = self.light_source.spotlight.start_angle(),
|
|
|
|
angle = self.light_source.spotlight.opening_angle(), tip_length = ARC_TIP_LENGTH)
|
2018-01-23 10:53:24 -08:00
|
|
|
#angle_arc.add_tip(at_start = True, at_end = True)
|
2018-02-06 11:12:50 +01:00
|
|
|
self.angle_arc.move_arc_center_to(self.light_source.source_point)
|
2018-01-23 10:53:24 -08:00
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# angle msmt (decimal number)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.angle_indicator = DecimalNumber(arc_angle / DEGREES,
|
2018-01-23 10:53:24 -08:00
|
|
|
num_decimal_points = 0,
|
|
|
|
unit = "^\\circ")
|
2018-02-06 11:12:50 +01:00
|
|
|
self.angle_indicator.next_to(self.angle_arc,RIGHT)
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
angle_update_func = lambda x: self.light_source.spotlight.opening_angle() / DEGREES
|
|
|
|
ca1 = ContinualChangingDecimal(self.angle_indicator,angle_update_func)
|
2018-01-29 15:20:57 +01:00
|
|
|
self.add(ca1)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
ca2 = AngleUpdater(self.angle_arc, self.light_source.spotlight)
|
2018-01-29 15:20:57 +01:00
|
|
|
self.add(ca2)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
ShowCreation(self.angle_arc),
|
|
|
|
ShowCreation(self.angle_indicator)
|
|
|
|
)
|
|
|
|
#self.add_foreground_mobject(self.angle_indicator)
|
|
|
|
self.wait()
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def rotate_screen(self):
|
2018-01-25 23:23:04 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(Rotate(self.light_source.spotlight.screen, TAU/8))
|
|
|
|
self.play(Rotate(self.light_source.spotlight.screen, -TAU/4))
|
|
|
|
self.play(Rotate(self.light_source.spotlight.screen, TAU/8))
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(Rotate(self.light_source.spotlight.screen, -TAU/4))
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(Rotate(self.light_source.spotlight.screen, TAU/4))
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-01-27 19:09:29 +01:00
|
|
|
### The following is supposed to morph the scene into the Earth scene,
|
2018-01-27 15:17:31 +01:00
|
|
|
### but it doesn't work
|
2018-01-23 10:53:24 -08:00
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
def morph_lighthouse_into_sun(self):
|
2018-01-23 10:53:24 -08:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-27 15:17:31 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
sun_position = [-100,0,0]
|
2018-01-27 13:30:25 +01:00
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
FadeOut(self.angle_arc),
|
|
|
|
FadeOut(self.angle_indicator)
|
|
|
|
)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.sun = LightSource(
|
|
|
|
opacity_function = inverse_quadratic(1,2,1),
|
|
|
|
num_levels = NUM_LEVELS,
|
|
|
|
radius = 150,
|
|
|
|
max_opacity_ambient = AMBIENT_FULL
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.sun.set_screen(self.light_source.spotlight.screen)
|
|
|
|
self.sun.spotlight.change_opacity_function(lambda r: 0.5)
|
|
|
|
#self.sun.set_radius(150)
|
|
|
|
self.sun.move_source_to(sun_position)
|
|
|
|
|
|
|
|
self.sun.spotlight.recalculate_sectors()
|
|
|
|
|
|
|
|
# temporarily remove the screen tracker while we move the source
|
|
|
|
self.remove(self.screen_tracker)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(self.light_source.lighthouse, self.sun.lighthouse),
|
|
|
|
ReplacementTransform(self.light_source.ambient_light, self.sun.ambient_light),
|
|
|
|
ReplacementTransform(self.light_source.spotlight, self.sun.spotlight),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.add(ScreenTracker(self.sun.spotlight))
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
class EarthScene(Scene):
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
SCREEN_THICKNESS = 10
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.screen_height = 2.0
|
|
|
|
self.brightness_rect_height = 1.0
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
# screen
|
2018-02-06 11:12:50 +01:00
|
|
|
self.screen = VMobject(stroke_color = WHITE, stroke_width = SCREEN_THICKNESS)
|
|
|
|
self.screen.set_points_as_corners([
|
|
|
|
[3,-self.screen_height/2,0],
|
|
|
|
[3,self.screen_height/2,0]
|
|
|
|
])
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Earth
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
earth_center_x = 2
|
|
|
|
earth_center = [earth_center_x,0,0]
|
|
|
|
earth_radius = 3
|
|
|
|
earth = Circle(radius = earth_radius)
|
|
|
|
earth.move_to(earth_center)
|
|
|
|
#self.remove(self.screen_tracker)
|
|
|
|
|
|
|
|
theta0 = 70 * DEGREES
|
|
|
|
dtheta = 10 * DEGREES
|
|
|
|
theta1 = theta0 + dtheta
|
|
|
|
theta = (theta0 + theta1)/2
|
|
|
|
|
|
|
|
earth.add(self.screen)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
# Morty
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
morty = Mortimer().scale(0.5).next_to(self.screen, RIGHT, buff = 1.5)
|
2018-01-27 13:30:25 +01:00
|
|
|
self.add_foreground_mobject(morty)
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
# Light source (far-away Sun)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
sun_position = [-100,0,0]
|
|
|
|
|
|
|
|
self.sun = LightSource(
|
2018-01-27 13:30:25 +01:00
|
|
|
opacity_function = lambda r : 0.5,
|
2018-02-06 11:12:50 +01:00
|
|
|
max_opacity_ambient = 0,
|
|
|
|
max_opacity_spotlight = 0.5,
|
2018-01-27 13:30:25 +01:00
|
|
|
num_levels = NUM_LEVELS,
|
2018-02-06 11:12:50 +01:00
|
|
|
radius = 150,
|
|
|
|
screen = self.screen
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.sun.move_source_to(sun_position)
|
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Add elements to scene
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.add(self.sun,self.screen)
|
|
|
|
self.bring_to_back(self.sun.spotlight.shadow)
|
|
|
|
screen_tracker = MovingScreenTracker(self.screen, light_source = self.sun)
|
|
|
|
sun_tracker = MovingSourceTracker(self.sun)
|
|
|
|
|
|
|
|
self.add(sun_tracker, screen_tracker)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(FadeIn(earth))
|
|
|
|
self.bring_to_back(earth)
|
|
|
|
|
|
|
|
# move screen onto Earth
|
|
|
|
screen_on_earth = self.screen.copy()
|
|
|
|
screen_on_earth.rotate(-theta)
|
|
|
|
screen_on_earth.scale(0.3)
|
|
|
|
screen_on_earth.move_to(np.array([
|
|
|
|
earth_center_x - earth_radius * np.cos(theta),
|
|
|
|
earth_radius * np.sin(theta),
|
|
|
|
0]))
|
|
|
|
|
|
|
|
polar_morty = morty.copy().scale(0.5).next_to(screen_on_earth,DOWN,buff = 0.5)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
Transform(self.screen, screen_on_earth),
|
|
|
|
Transform(morty,polar_morty)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
tropical_morty = polar_morty.copy()
|
|
|
|
tropical_morty.move_to(np.array([0,0,0]))
|
|
|
|
morty.target = tropical_morty
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
# move screen to equator
|
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
Rotate(earth, theta0 + dtheta/2,run_time = 3),
|
|
|
|
MoveToTarget(morty, path_arc = 70*DEGREES, run_time = 3),
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
class ScreenShapingScene(ThreeDScene):
|
|
|
|
|
|
|
|
|
|
|
|
# TODO: Morph from Earth Scene into this scene
|
2018-01-27 13:30:25 +01:00
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.setup_elements()
|
|
|
|
self.deform_screen()
|
|
|
|
self.create_brightness_rect()
|
|
|
|
self.slant_screen()
|
|
|
|
self.unslant_screen()
|
|
|
|
self.left_shift_screen_while_showing_light_indicator()
|
|
|
|
self.add_distance_arrow()
|
|
|
|
self.right_shift_screen_while_showing_light_indicator_and_distance_arrow()
|
|
|
|
self.left_shift_again()
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
self.morph_into_3d()
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def setup_elements(self):
|
2018-01-27 15:17:31 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
SCREEN_THICKNESS = 10
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.screen_height = 1.0
|
|
|
|
self.brightness_rect_height = 1.0
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# screen
|
|
|
|
self.screen = Line([3,-self.screen_height/2,0],[3,self.screen_height/2,0],
|
|
|
|
path_arc = 0, num_arc_anchors = 10)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
# light source
|
|
|
|
self.light_source = LightSource(
|
2018-01-27 13:30:25 +01:00
|
|
|
opacity_function = inverse_quadratic(1,5,1),
|
|
|
|
num_levels = NUM_LEVELS,
|
|
|
|
radius = 10,
|
2018-01-29 15:20:57 +01:00
|
|
|
screen = self.screen
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
2018-02-06 11:12:50 +01:00
|
|
|
self.light_source.move_source_to([-5,0,0])
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
# abbreviations
|
|
|
|
self.ambient_light = self.light_source.ambient_light
|
|
|
|
self.spotlight = self.light_source.spotlight
|
|
|
|
self.lighthouse = self.light_source.lighthouse
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
screen_tracker = ScreenTracker(self.spotlight)
|
|
|
|
self.add(screen_tracker)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Morty
|
|
|
|
self.morty = Mortimer().scale(0.3).next_to(self.screen, RIGHT, buff = 0.5)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# Add everything to the scene
|
2018-02-06 11:12:50 +01:00
|
|
|
self.add(self.ambient_light, self.lighthouse)
|
2018-01-29 15:20:57 +01:00
|
|
|
self.add_foreground_mobject(self.morty)
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeIn(self.screen))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
dimmed_ambient_light = self.ambient_light.copy()
|
|
|
|
dimmed_ambient_light.dimming(AMBIENT_DIMMED)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
FadeIn(self.spotlight), # this hides Morty for a moment, why?
|
|
|
|
Transform(self.ambient_light,dimmed_ambient_light)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wait()
|
2018-01-27 13:30:25 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def deform_screen(self):
|
2018-01-27 13:30:25 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.wait()
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.play(ApplyMethod(self.screen.set_path_arc, 45 * DEGREES))
|
|
|
|
self.play(ApplyMethod(self.screen.set_path_arc, -90 * DEGREES))
|
|
|
|
self.play(ApplyMethod(self.screen.set_path_arc, 0))
|
2018-01-21 18:29:13 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def create_brightness_rect(self):
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
# in preparation for the slanting, create a rectangle that shows the brightness
|
|
|
|
|
|
|
|
# a rect a zero width overlaying the screen
|
|
|
|
# so we can morph it into the brightness rect above
|
|
|
|
brightness_rect0 = Rectangle(width = 0,
|
|
|
|
height = self.screen_height).move_to(self.screen.get_center())
|
|
|
|
self.add_foreground_mobject(brightness_rect0)
|
|
|
|
|
|
|
|
self.brightness_rect = Rectangle(width = self.brightness_rect_height,
|
|
|
|
height = self.brightness_rect_height, fill_color = YELLOW, fill_opacity = 0.5)
|
|
|
|
|
|
|
|
self.brightness_rect.next_to(self.screen, UP, buff = 1)
|
2018-01-27 13:30:25 +01:00
|
|
|
|
|
|
|
self.play(
|
2018-01-29 15:20:57 +01:00
|
|
|
ReplacementTransform(brightness_rect0,self.brightness_rect)
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.unslanted_screen = self.screen.copy()
|
|
|
|
self.unslanted_brightness_rect = self.brightness_rect.copy()
|
2018-01-27 15:17:31 +01:00
|
|
|
# for unslanting the screen later
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
def slant_screen(self):
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
SLANTING_AMOUNT = 0.1
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
lower_screen_point, upper_screen_point = self.screen.get_start_and_end()
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
lower_slanted_screen_point = interpolate(
|
2018-02-06 11:12:50 +01:00
|
|
|
lower_screen_point, self.spotlight.source_point, SLANTING_AMOUNT
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
|
|
|
upper_slanted_screen_point = interpolate(
|
2018-02-06 11:12:50 +01:00
|
|
|
upper_screen_point, self.spotlight.source_point, -SLANTING_AMOUNT
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.slanted_brightness_rect = self.brightness_rect.copy()
|
|
|
|
self.slanted_brightness_rect.width *= 2
|
|
|
|
self.slanted_brightness_rect.generate_points()
|
|
|
|
self.slanted_brightness_rect.set_fill(opacity = 0.25)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.slanted_screen = Line(lower_slanted_screen_point,upper_slanted_screen_point,
|
2018-01-27 15:17:31 +01:00
|
|
|
path_arc = 0, num_arc_anchors = 10)
|
2018-01-29 15:20:57 +01:00
|
|
|
self.slanted_brightness_rect.move_to(self.brightness_rect.get_center())
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-27 13:30:25 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
Transform(self.screen,self.slanted_screen),
|
|
|
|
Transform(self.brightness_rect,self.slanted_brightness_rect),
|
2018-01-27 13:30:25 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
|
|
|
|
2018-01-27 19:09:29 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def unslant_screen(self):
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
2018-01-27 15:17:31 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
Transform(self.screen,self.unslanted_screen),
|
|
|
|
Transform(self.brightness_rect,self.unslanted_brightness_rect),
|
2018-01-27 15:17:31 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
|
|
|
|
|
|
|
|
def left_shift_screen_while_showing_light_indicator(self):
|
2018-01-27 19:09:29 +01:00
|
|
|
|
|
|
|
# Scene 5: constant screen size, changing opening angle
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
OPACITY_FOR_UNIT_INTENSITY = 1
|
|
|
|
|
2018-01-27 19:09:29 +01:00
|
|
|
# let's use an actual light indicator instead of just rects
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.indicator_intensity = 0.25
|
|
|
|
indicator_height = 1.25 * self.screen_height
|
2018-01-27 19:09:29 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.indicator = LightIndicator(radius = indicator_height/2,
|
2018-01-27 19:09:29 +01:00
|
|
|
opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
|
|
|
|
color = LIGHT_COLOR,
|
|
|
|
precision = 2)
|
2018-01-29 15:20:57 +01:00
|
|
|
self.indicator.set_intensity(self.indicator_intensity)
|
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.indicator.move_to(self.brightness_rect.get_center())
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-27 15:17:31 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
FadeOut(self.brightness_rect),
|
2018-01-29 15:20:57 +01:00
|
|
|
FadeIn(self.indicator)
|
2018-01-27 15:17:31 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
# Here some digits of the indicator disappear...
|
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.add_foreground_mobject(self.indicator.reading)
|
2018-01-27 19:09:29 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
|
|
|
|
self.unit_indicator_intensity = 1.0 # intensity at distance 1
|
|
|
|
# (where we are about to move to)
|
2018-01-27 19:09:29 +01:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
self.left_shift = (self.screen.get_center()[0] - self.spotlight.source_point[0])/2
|
2018-01-27 15:17:31 +01:00
|
|
|
|
2018-01-27 19:09:29 +01:00
|
|
|
self.play(
|
2018-02-06 11:12:50 +01:00
|
|
|
self.screen.shift,[-self.left_shift,0,0],
|
|
|
|
self.morty.shift,[-self.left_shift,0,0],
|
|
|
|
self.indicator.shift,[-self.left_shift,0,0],
|
|
|
|
self.indicator.set_intensity,self.unit_indicator_intensity,
|
2018-01-27 19:09:29 +01:00
|
|
|
)
|
|
|
|
|
2018-01-27 15:17:31 +01:00
|
|
|
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-01-29 15:20:57 +01:00
|
|
|
def add_distance_arrow(self):
|
|
|
|
|
|
|
|
# distance arrow (length 1)
|
|
|
|
left_x = self.spotlight.source_point[0]
|
|
|
|
right_x = self.screen.get_center()[0]
|
|
|
|
arrow_y = -2
|
|
|
|
arrow1 = Arrow([left_x,arrow_y,0],[right_x,arrow_y,0])
|
|
|
|
arrow2 = Arrow([right_x,arrow_y,0],[left_x,arrow_y,0])
|
|
|
|
arrow1.set_fill(color = WHITE)
|
|
|
|
arrow2.set_fill(color = WHITE)
|
|
|
|
distance_decimal = Integer(1).next_to(arrow1,DOWN)
|
|
|
|
self.arrow = VGroup(arrow1, arrow2,distance_decimal)
|
|
|
|
self.add(self.arrow)
|
|
|
|
|
|
|
|
|
|
|
|
# distance arrow (length 2)
|
|
|
|
# will be morphed into
|
|
|
|
self.distance_to_source = right_x - left_x
|
|
|
|
new_right_x = left_x + 2 * self.distance_to_source
|
|
|
|
new_arrow1 = Arrow([left_x,arrow_y,0],[new_right_x,arrow_y,0])
|
|
|
|
new_arrow2 = Arrow([new_right_x,arrow_y,0],[left_x,arrow_y,0])
|
|
|
|
new_arrow1.set_fill(color = WHITE)
|
|
|
|
new_arrow2.set_fill(color = WHITE)
|
|
|
|
new_distance_decimal = Integer(2).next_to(new_arrow1,DOWN)
|
|
|
|
self.new_arrow = VGroup(new_arrow1, new_arrow2, new_distance_decimal)
|
|
|
|
# don't add it yet
|
|
|
|
|
|
|
|
|
|
|
|
def right_shift_screen_while_showing_light_indicator_and_distance_arrow(self):
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(self.arrow,self.new_arrow),
|
|
|
|
ApplyMethod(self.screen.shift,[self.distance_to_source,0,0]),
|
|
|
|
#ApplyMethod(self.indicator.shift,[self.left_shift,0,0]),
|
|
|
|
|
|
|
|
ApplyMethod(self.indicator.set_intensity,self.indicator_intensity),
|
|
|
|
# this should trigger ChangingDecimal, but it doesn't
|
|
|
|
# maybe bc it's an anim within an anim?
|
|
|
|
|
|
|
|
ApplyMethod(self.morty.shift,[self.distance_to_source,0,0]),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def left_shift_again(self):
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
2018-01-27 15:17:31 +01:00
|
|
|
self.play(
|
2018-01-29 15:20:57 +01:00
|
|
|
ReplacementTransform(self.new_arrow,self.arrow),
|
|
|
|
ApplyMethod(self.screen.shift,[-self.distance_to_source,0,0]),
|
|
|
|
#ApplyMethod(self.indicator.shift,[-self.left_shift,0,0]),
|
|
|
|
ApplyMethod(self.indicator.set_intensity,self.unit_indicator_intensity),
|
|
|
|
ApplyMethod(self.morty.shift,[-self.distance_to_source,0,0]),
|
2018-01-27 15:17:31 +01:00
|
|
|
)
|
2018-01-21 18:29:13 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
def morph_into_3d(self):
|
|
|
|
|
|
|
|
axes = ThreeDAxes()
|
|
|
|
self.add(axes)
|
|
|
|
|
|
|
|
phi0 = self.camera.get_phi() # default is 0 degs
|
|
|
|
theta0 = self.camera.get_theta() # default is -90 degs
|
|
|
|
distance0 = self.camera.get_distance()
|
|
|
|
|
|
|
|
self.play(FadeOut(self.ambient_light))
|
|
|
|
|
|
|
|
|
|
|
|
phi1 = 60 * DEGREES # angle from zenith (0 to 180)
|
|
|
|
theta1 = -135 * DEGREES # azimuth (0 to 360)
|
|
|
|
distance1 = distance0
|
|
|
|
target_point = self.camera.get_spherical_coords(phi1, theta1, distance1)
|
|
|
|
|
|
|
|
dphi = phi1 - phi0
|
|
|
|
dtheta = theta1 - theta0
|
|
|
|
|
|
|
|
print "moving camera from (", phi0/DEGREES, ", ", theta0/DEGREES, ") to (", phi1/DEGREES, ", ", theta1/DEGREES, ")"
|
|
|
|
|
|
|
|
# camera_tracker = MovingCameraTracker(self.camera.rotation_mobject,
|
|
|
|
# light_source = self.spotlight.source_point,
|
|
|
|
# screen = self.screen)
|
|
|
|
|
|
|
|
camera_target_point = target_point # self.camera.get_spherical_coords(45 * DEGREES, -60 * DEGREES)
|
|
|
|
projection_direction = self.camera.spherical_coords_to_point(phi1,theta1, 1)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ApplyMethod(self.camera.rotation_mobject.move_to, camera_target_point)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class BackToEulerSumScene(PiCreatureScene):
|
|
|
|
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
#self.remove(self.get_primary_pi_creature())
|
|
|
|
|
|
|
|
NUM_CONES = 7
|
|
|
|
NUM_VISIBLE_CONES = 6
|
|
|
|
INDICATOR_RADIUS = 0.5
|
|
|
|
OPACITY_FOR_UNIT_INTENSITY = 1.0
|
|
|
|
|
|
|
|
self.number_line = NumberLine(
|
|
|
|
x_min = 0,
|
|
|
|
color = WHITE,
|
|
|
|
number_at_center = 1.6,
|
|
|
|
stroke_width = 1,
|
|
|
|
numbers_with_elongated_ticks = range(1,5),
|
|
|
|
numbers_to_show = range(1,5),
|
|
|
|
unit_size = 2,
|
|
|
|
tick_frequency = 0.2,
|
|
|
|
line_to_number_buff = LARGE_BUFF,
|
|
|
|
label_direction = UP,
|
|
|
|
)
|
|
|
|
|
|
|
|
self.number_line.label_direction = DOWN
|
|
|
|
self.number_line.shift(3*UP)
|
|
|
|
|
|
|
|
self.number_line_labels = self.number_line.get_number_mobjects()
|
|
|
|
self.add(self.number_line,self.number_line_labels)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
origin_point = self.number_line.number_to_point(0)
|
|
|
|
|
|
|
|
self.default_pi_creature_class = Randolph
|
|
|
|
randy = self.get_primary_pi_creature()
|
|
|
|
|
|
|
|
randy.scale(0.5)
|
|
|
|
randy.flip()
|
|
|
|
right_pupil = randy.pupils[1]
|
|
|
|
randy.next_to(origin_point, LEFT, buff = 0, submobject_to_align = right_pupil)
|
|
|
|
|
|
|
|
randy_copy = randy.copy()
|
|
|
|
randy_copy.target = randy.copy().shift(DOWN)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bubble = ThoughtBubble(direction = RIGHT,
|
|
|
|
width = 4, height = 3,
|
|
|
|
file_name = "Bubbles_thought.svg")
|
|
|
|
bubble.next_to(randy,DOWN+LEFT)
|
|
|
|
bubble.set_fill(color = BLACK, opacity = 1)
|
|
|
|
|
|
|
|
# self.play(
|
|
|
|
# randy.change, "wave_2",
|
|
|
|
# ShowCreation(bubble),
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
|
|
euler_sum = TexMobject("1", "+", "{1\over 4}",
|
|
|
|
"+", "{1\over 9}", "+", "{1\over 16}", "+", "{1\over 25}", "+", "\cdots", " ")
|
|
|
|
# the last entry is a dummy element which makes looping easier
|
|
|
|
# used just for putting the fractions into the light indicators
|
|
|
|
|
|
|
|
intensities = np.array([1./(n+1)**2 for n in range(NUM_CONES)])
|
|
|
|
opacities = intensities * OPACITY_FOR_UNIT_INTENSITY
|
|
|
|
|
|
|
|
# repeat:
|
|
|
|
|
|
|
|
# fade in lighthouse
|
|
|
|
# switch on / fade in ambient light
|
|
|
|
# show creation / write light indicator
|
|
|
|
# move indicator onto origin
|
|
|
|
# while morphing and dimming
|
|
|
|
# move indicator into thought bubble
|
|
|
|
# while indicators already inside shift to the back
|
|
|
|
# and while term appears in the series below
|
|
|
|
|
|
|
|
point = self.number_line.number_to_point(1)
|
|
|
|
v = point - self.number_line.number_to_point(0)
|
|
|
|
light_source = LightSource()
|
|
|
|
light_source.move_source_to(point)
|
|
|
|
light_source.ambient_light.move_source_to(point)
|
|
|
|
light_source.lighthouse.move_to(point)
|
|
|
|
|
|
|
|
self.play(FadeIn(light_source.lighthouse))
|
|
|
|
self.play(SwitchOn(light_source.ambient_light))
|
|
|
|
|
|
|
|
for i in range(2,NUM_VISIBLE_CONES + 1):
|
|
|
|
|
|
|
|
point = self.number_line.number_to_point(i)
|
|
|
|
|
|
|
|
moving_light_source = light_source.deepcopy()
|
|
|
|
moving_light_source.set_max_opacity_ambient(0.0001)
|
|
|
|
self.add(moving_light_source.ambient_light)
|
|
|
|
target_light_source = moving_light_source.deepcopy()
|
|
|
|
# # target_light_source.ambient_light.dimming(OPACITY_FOR_UNIT_INTENSITY)
|
|
|
|
# #target_light_source.move_to(point)
|
|
|
|
# target_light_source.move_source_to(point)
|
|
|
|
target_light_source.move_source_to(point)
|
|
|
|
target_light_source.set_max_opacity_ambient(0.5)
|
|
|
|
# target_light_source.lighthouse.move_to(point)
|
|
|
|
# target_light_source.set_max_opacity_ambient(1.0)
|
|
|
|
# #target_light_source.ambient_light.generate_points()
|
|
|
|
|
|
|
|
moving_light_source.ambient_light.target = target_light_source.ambient_light
|
|
|
|
|
|
|
|
# #self.play(MoveToTarget(randy_copy))
|
|
|
|
self.play(
|
|
|
|
Transform(moving_light_source, target_light_source),
|
|
|
|
#MoveToTarget(moving_light_source.ambient_light),
|
|
|
|
)
|
|
|
|
|
|
|
|
# light_source = moving_light_source
|
|
|
|
|
|
|
|
|
|
|
|
# switch on lights off-screen
|
|
|
|
# while writing an ellipsis in the series
|
|
|
|
# and fading out the stack of indicators
|
|
|
|
# and fading in pi^2/6 instead
|
|
|
|
# move a copy of pi^2/6 down to the series
|
|
|
|
# ans fade in an equals sign
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# for i in range(1,NUM_CONES+1):
|
|
|
|
# lighthouse = Lighthouse()
|
|
|
|
# point = self.number_line.number_to_point(i)
|
|
|
|
# ambient_light = AmbientLight(
|
|
|
|
# opacity_function = inverse_quadratic(1,2,1),
|
|
|
|
# num_levels = NUM_LEVELS,
|
|
|
|
# radius = 12.0)
|
|
|
|
|
|
|
|
# ambient_light.move_source_to(point)
|
|
|
|
# lighthouse.next_to(point,DOWN,0)
|
|
|
|
# lighthouses.append(lighthouse)
|
|
|
|
# ambient_lights.append(ambient_light)
|
|
|
|
# lighthouse_pos.append(point)
|
|
|
|
|
|
|
|
# for i in range(1,NUM_VISIBLE_CONES+1):
|
|
|
|
|
|
|
|
# # create light indicators
|
|
|
|
# # but they contain fractions!
|
|
|
|
# indicator = LightIndicator(color = LIGHT_COLOR,
|
|
|
|
# radius = INDICATOR_RADIUS,
|
|
|
|
# opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
|
|
|
|
# show_reading = False
|
|
|
|
# )
|
|
|
|
# indicator.set_intensity(intensities[i-1])
|
|
|
|
# indicator_reading = euler_sum[-2+2*i]
|
|
|
|
# indicator_reading.scale_to_fit_height(0.8*indicator.get_height())
|
|
|
|
# indicator_reading.move_to(indicator.get_center())
|
|
|
|
# indicator.add(indicator_reading)
|
|
|
|
# indicator.foreground.set_fill(None,opacities[i-1])
|
|
|
|
|
|
|
|
|
|
|
|
# if i == 1:
|
|
|
|
# indicator.next_to(randy,DOWN,buff = 5)
|
|
|
|
# indicator_reading.scale_to_fit_height(0.4*indicator.get_height())
|
|
|
|
# # otherwise we get a huge 1
|
|
|
|
# else:
|
|
|
|
# indicator.next_to(light_indicators[i-2],DOWN, buff = 0.2)
|
|
|
|
|
|
|
|
# light_indicators.append(indicator)
|
|
|
|
# indicators_as_mob.add(indicator)
|
|
|
|
|
|
|
|
|
|
|
|
# bubble.add_content(indicators_as_mob)
|
|
|
|
# indicators_as_mob.shift(DOWN+0.5*LEFT)
|
|
|
|
|
|
|
|
|
|
|
|
# for lh in lighthouses:
|
|
|
|
# self.add_foreground_mobject(lh)
|
|
|
|
|
|
|
|
|
|
|
|
# # slowly switch on visible light cones and increment indicator
|
|
|
|
# for (i,ambient_light) in zip(range(NUM_VISIBLE_CONES),ambient_lights[:NUM_VISIBLE_CONES]):
|
|
|
|
# indicator_start_time = 0.4 * (i+1) * SWITCH_ON_RUN_TIME/ambient_light.radius * self.number_line.unit_size
|
|
|
|
# indicator_stop_time = indicator_start_time + INDICATOR_UPDATE_TIME
|
|
|
|
# indicator_rate_func = squish_rate_func(
|
|
|
|
# smooth,indicator_start_time,indicator_stop_time)
|
|
|
|
# self.play(
|
|
|
|
# SwitchOn(ambient_light),
|
|
|
|
# FadeIn(light_indicators[i])
|
|
|
|
# )
|
|
|
|
|
|
|
|
# # quickly switch on off-screen light cones and increment indicator
|
|
|
|
# for (i,ambient_light) in zip(range(NUM_VISIBLE_CONES,NUM_CONES),ambient_lights[NUM_VISIBLE_CONES:NUM_CONES]):
|
|
|
|
# indicator_start_time = 0.5 * (i+1) * FAST_SWITCH_ON_RUN_TIME/ambient_light.radius * self.number_line.unit_size
|
|
|
|
# indicator_stop_time = indicator_start_time + FAST_INDICATOR_UPDATE_TIME
|
|
|
|
# indicator_rate_func = squish_rate_func(#smooth, 0.8, 0.9)
|
|
|
|
# smooth,indicator_start_time,indicator_stop_time)
|
|
|
|
# self.play(
|
|
|
|
# SwitchOn(ambient_light, run_time = FAST_SWITCH_ON_RUN_TIME),
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
|
|
# # show limit value in light indicator and an equals sign
|
|
|
|
# sum_indicator = LightIndicator(color = LIGHT_COLOR,
|
|
|
|
# radius = INDICATOR_RADIUS,
|
|
|
|
# opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
|
|
|
|
# show_reading = False
|
|
|
|
# )
|
|
|
|
# sum_indicator.set_intensity(intensities[0] * np.pi**2/6)
|
|
|
|
# sum_indicator_reading = TexMobject("{\pi^2 \over 6}")
|
|
|
|
# sum_indicator_reading.scale_to_fit_height(0.8 * sum_indicator.get_height())
|
|
|
|
# sum_indicator.add(sum_indicator_reading)
|
|
|
|
|
|
|
|
# brace = Brace(indicators_as_mob, direction = RIGHT, buff = 0.5)
|
|
|
|
# brace.shift(2*RIGHT)
|
|
|
|
# sum_indicator.next_to(brace,RIGHT)
|
|
|
|
|
|
|
|
|
|
|
|
# self.play(
|
|
|
|
# ShowCreation(brace),
|
|
|
|
# ShowCreation(sum_indicator), # DrawBorderThenFill
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TwoLightSourcesScene(PiCreatureScene):
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
|
|
|
|
MAX_OPACITY = 0.4
|
|
|
|
INDICATOR_RADIUS = 0.6
|
|
|
|
OPACITY_FOR_UNIT_INTENSITY = 0.5
|
|
|
|
|
|
|
|
A = np.array([5,-3,0])
|
|
|
|
B = np.array([-5,3,0])
|
|
|
|
C = np.array([-5,-3,0])
|
|
|
|
|
|
|
|
morty = self.get_primary_pi_creature()
|
|
|
|
morty.scale(0.3).flip().move_to(C)
|
|
|
|
|
|
|
|
horizontal = VMobject(stroke_width = 1)
|
|
|
|
horizontal.set_points_as_corners([C,A])
|
|
|
|
vertical = VMobject(stroke_width = 1)
|
|
|
|
vertical.set_points_as_corners([C,B])
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(horizontal),
|
|
|
|
ShowCreation(vertical)
|
|
|
|
)
|
|
|
|
|
|
|
|
indicator = LightIndicator(color = LIGHT_COLOR,
|
|
|
|
radius = INDICATOR_RADIUS,
|
|
|
|
opacity_for_unit_intensity = OPACITY_FOR_UNIT_INTENSITY,
|
|
|
|
show_reading = True,
|
|
|
|
precision = 2
|
|
|
|
)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
indicator.next_to(morty,LEFT)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
Write(indicator)
|
|
|
|
)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
ambient_light1 = AmbientLight(max_opacity = MAX_OPACITY)
|
|
|
|
ambient_light1.move_source_to(A)
|
|
|
|
ambient_light2 = AmbientLight(max_opacity = MAX_OPACITY)
|
|
|
|
ambient_light2.move_source_to(B)
|
|
|
|
lighthouse1 = Lighthouse()
|
|
|
|
lighthouse1.next_to(A,DOWN,buff = 0)
|
|
|
|
lighthouse2 = Lighthouse()
|
|
|
|
lighthouse2.next_to(B,DOWN,buff = 0)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
FadeIn(lighthouse1),
|
|
|
|
FadeIn(lighthouse2),
|
|
|
|
SwitchOn(ambient_light1),
|
|
|
|
SwitchOn(ambient_light2)
|
|
|
|
)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
UpdateLightIndicator(indicator,1.5)
|
|
|
|
)
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.wait()
|
2018-01-29 15:20:57 +01:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
new_indicator = indicator.copy()
|
|
|
|
self.add(new_indicator)
|
|
|
|
self.play(
|
|
|
|
indicator.shift, 2 * UP
|
|
|
|
)
|
|
|
|
|
|
|
|
ambient_light3 = AmbientLight(max_opacity = MAX_OPACITY)
|
|
|
|
lighthouse3 = Lighthouse()
|
|
|
|
lighthouse3.next_to(ambient_light3,DOWN,buff = 0)
|
|
|
|
ambient_light3.add(lighthouse3)
|
|
|
|
#moving_light.move_to(np.array([6,3.5,0]))
|
2018-01-17 15:18:02 -08:00
|
|
|
|
2018-02-06 11:12:50 +01:00
|
|
|
self.play(
|
|
|
|
FadeOut(ambient_light1),
|
|
|
|
FadeOut(lighthouse1),
|
|
|
|
FadeOut(ambient_light2),
|
|
|
|
FadeOut(lighthouse2),
|
|
|
|
|
|
|
|
FadeIn(ambient_light3),
|
|
|
|
UpdateLightIndicator(new_indicator,0.0)
|
|
|
|
)
|
|
|
|
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
ambient_light3.shift,UP+RIGHT
|
|
|
|
)
|
2018-01-17 15:18:02 -08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|