2017-08-05 20:47:06 -07:00
|
|
|
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 *
|
|
|
|
from animation.playground import *
|
|
|
|
from topics.geometry import *
|
|
|
|
from topics.characters import *
|
|
|
|
from topics.functions import *
|
|
|
|
from topics.fractals import *
|
|
|
|
from topics.number_line import *
|
|
|
|
from topics.combinatorics import *
|
|
|
|
from topics.numerals import *
|
|
|
|
from topics.three_dimensions import *
|
|
|
|
from topics.objects import *
|
|
|
|
from topics.probability import *
|
|
|
|
from topics.complex_numbers import *
|
|
|
|
from topics.common_scenes import *
|
|
|
|
from scene import Scene
|
|
|
|
from scene.reconfigurable_scene import ReconfigurableScene
|
|
|
|
from scene.zoomed_scene import *
|
|
|
|
from camera import Camera
|
|
|
|
from mobject.svg_mobject import *
|
|
|
|
from mobject.tex_mobject import *
|
|
|
|
|
|
|
|
##########
|
2017-08-07 16:17:35 -07:00
|
|
|
#force_skipping
|
2017-08-05 20:47:06 -07:00
|
|
|
#revert_to_original_skipping_status
|
|
|
|
|
2017-08-07 16:17:35 -07:00
|
|
|
##########
|
|
|
|
|
|
|
|
class Slider(NumberLine):
|
|
|
|
CONFIG = {
|
|
|
|
"color" : WHITE,
|
|
|
|
"x_min" : -1,
|
|
|
|
"x_max" : 1,
|
|
|
|
"unit_size" : 2,
|
|
|
|
"center_value" : 0,
|
|
|
|
"number_scale_val" : 0.75,
|
2017-08-07 23:51:58 -07:00
|
|
|
"label_scale_val" : 1,
|
2017-08-07 16:17:35 -07:00
|
|
|
"numbers_with_elongated_ticks" : [],
|
|
|
|
"line_to_number_vect" : LEFT,
|
|
|
|
"line_to_number_buff" : MED_LARGE_BUFF,
|
|
|
|
"dial_radius" : 0.1,
|
|
|
|
"dial_color" : YELLOW,
|
|
|
|
"include_real_estate_ticks" : True,
|
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
NumberLine.__init__(self, **kwargs)
|
|
|
|
self.rotate(np.pi/2)
|
|
|
|
self.init_dial()
|
|
|
|
if self.include_real_estate_ticks:
|
|
|
|
self.add_real_estate_ticks()
|
|
|
|
|
|
|
|
def init_dial(self):
|
|
|
|
dial = Dot(
|
|
|
|
radius = self.dial_radius,
|
|
|
|
color = self.dial_color,
|
|
|
|
)
|
|
|
|
dial.move_to(self.number_to_point(0))
|
|
|
|
re_dial = dial.copy()
|
|
|
|
re_dial.set_fill(opacity = 0)
|
|
|
|
self.add(dial, re_dial)
|
|
|
|
|
|
|
|
self.dial = dial
|
|
|
|
self.re_dial = re_dial
|
|
|
|
self.last_sign = -1
|
|
|
|
|
2017-08-07 17:16:31 -07:00
|
|
|
def add_label(self, tex):
|
|
|
|
label = TexMobject(tex)
|
|
|
|
label.scale(self.label_scale_val)
|
|
|
|
label.move_to(self.main_line.get_top())
|
|
|
|
label.shift(MED_LARGE_BUFF*UP)
|
|
|
|
self.add(label)
|
|
|
|
self.label = label
|
|
|
|
|
2017-08-07 16:17:35 -07:00
|
|
|
def add_real_estate_ticks(
|
|
|
|
self,
|
|
|
|
re_per_tick = 0.05,
|
|
|
|
colors = [BLUE, RED],
|
|
|
|
):
|
|
|
|
self.real_estate_ticks = VGroup(*[
|
2017-08-07 23:51:58 -07:00
|
|
|
self.get_tick(u*np.sqrt(x + re_per_tick))
|
|
|
|
for x in np.arange(0, 1, re_per_tick)
|
2017-08-07 16:17:35 -07:00
|
|
|
for u in [-1, 1]
|
|
|
|
])
|
|
|
|
self.real_estate_ticks.set_stroke(width = 3)
|
|
|
|
self.real_estate_ticks.gradient_highlight(*colors)
|
|
|
|
self.add(self.real_estate_ticks)
|
|
|
|
self.add(self.dial)
|
|
|
|
return self.real_estate_ticks
|
|
|
|
|
|
|
|
def set_value(self, x):
|
|
|
|
for dial, val in (self.dial, x), (self.re_dial, x**2):
|
|
|
|
dial.move_to(self.number_to_point(val))
|
|
|
|
return self
|
|
|
|
|
|
|
|
def change_real_estate(self, d_re):
|
|
|
|
left_over = 0
|
|
|
|
curr_re = self.get_real_estate()
|
|
|
|
if d_re < -curr_re:
|
|
|
|
left_over = d_re + curr_re
|
|
|
|
d_re = -curr_re
|
|
|
|
self.set_real_estate(curr_re + d_re)
|
|
|
|
return left_over
|
|
|
|
|
|
|
|
def set_real_estate(self, target_re):
|
|
|
|
if target_re < 0:
|
|
|
|
raise Exception("Cannot set real estate below 0")
|
|
|
|
self.re_dial.move_to(self.number_to_point(target_re))
|
|
|
|
self.update_dial_by_re_dial()
|
|
|
|
return self
|
|
|
|
|
|
|
|
def get_dial_supplement_animation(self):
|
|
|
|
return UpdateFromFunc(self.dial, self.update_dial_by_re_dial)
|
|
|
|
|
|
|
|
def update_dial_by_re_dial(self, dial = None):
|
|
|
|
dial = dial or self.dial
|
|
|
|
re = self.get_real_estate()
|
|
|
|
sign = np.sign(self.get_value() - self.center_value)
|
|
|
|
if sign == 0:
|
|
|
|
sign = -self.last_sign
|
|
|
|
self.last_sign *= -1
|
|
|
|
dial.move_to(self.number_to_point(
|
|
|
|
self.center_value + sign*np.sqrt(re)
|
|
|
|
))
|
|
|
|
return dial
|
|
|
|
|
|
|
|
def get_value(self):
|
|
|
|
return self.point_to_number(self.dial.get_center())
|
|
|
|
|
|
|
|
def get_real_estate(self):
|
|
|
|
return self.point_to_number(self.re_dial.get_center())
|
|
|
|
|
|
|
|
def copy(self):
|
|
|
|
return self.deepcopy()
|
|
|
|
|
|
|
|
class SliderScene(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"n_sliders" : 4,
|
|
|
|
"slider_spacing" : MED_LARGE_BUFF,
|
|
|
|
"slider_config" : {},
|
|
|
|
"center_point" : None,
|
|
|
|
"total_real_estate" : 1,
|
|
|
|
"ambiently_change_sliders" : False,
|
|
|
|
"ambient_velocity_magnitude" : 1.0,
|
|
|
|
"ambient_acceleration_magnitude" : 1.0,
|
|
|
|
"ambient_jerk_magnitude" : 1.0/2,
|
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
if self.center_point is None:
|
|
|
|
self.center_point = np.zeros(self.n_sliders)
|
|
|
|
sliders = VGroup(*[
|
|
|
|
Slider(center_value = cv, **self.slider_config)
|
|
|
|
for cv in self.center_point
|
|
|
|
])
|
|
|
|
sliders.arrange_submobjects(RIGHT, buff = self.slider_spacing)
|
|
|
|
sliders[0].add_numbers()
|
|
|
|
sliders[0].set_value(np.sqrt(self.total_real_estate))
|
|
|
|
self.sliders = sliders
|
2017-08-07 17:16:31 -07:00
|
|
|
|
|
|
|
self.add_labels_to_sliders()
|
|
|
|
self.add(sliders)
|
|
|
|
|
|
|
|
def add_labels_to_sliders(self):
|
|
|
|
if len(self.sliders) <= 4:
|
|
|
|
for slider, char in zip(self.sliders, "xyzw"):
|
|
|
|
slider.add_label(char)
|
|
|
|
else:
|
|
|
|
for i, slider in enumerate(self.sliders):
|
|
|
|
slider.add_label("x_{%d}"%(i+1))
|
|
|
|
return self
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
def reset_dials(self, values, run_time = 1, **kwargs):
|
|
|
|
target_vector = self.get_target_vect_from_subset_of_values(values, **kwargs)
|
|
|
|
self.play(*[
|
|
|
|
ApplyMethod(slider.set_value, value)
|
|
|
|
for value, slider in zip(target_vector, self.sliders)
|
|
|
|
] + [
|
|
|
|
slider.get_dial_supplement_animation()
|
|
|
|
for slider in self.sliders
|
|
|
|
], run_time = run_time)
|
|
|
|
|
|
|
|
def get_target_vect_from_subset_of_values(self, values, fixed_indices = None):
|
|
|
|
if fixed_indices is None: fixed_indices = []
|
|
|
|
n = len(self.sliders)
|
|
|
|
curr_vector = self.get_vector()
|
|
|
|
target_vector = np.zeros(n)
|
|
|
|
unspecified_vector = np.zeros(n)
|
|
|
|
for i in range(n):
|
|
|
|
if i < len(values) and values[i] is not None:
|
|
|
|
target_vector[i] = values[i]
|
|
|
|
else:
|
|
|
|
unspecified_vector[i] = curr_vector[i]
|
|
|
|
left_over_re = self.total_real_estate - np.linalg.norm(target_vector)**2
|
|
|
|
if left_over_re < 0:
|
2017-08-07 16:17:35 -07:00
|
|
|
raise Exception("Overspecified reset")
|
2017-08-07 23:51:58 -07:00
|
|
|
uv_norm = np.linalg.norm(unspecified_vector)
|
|
|
|
if uv_norm > 0:
|
|
|
|
unspecified_vector *= np.sqrt(left_over_re)/uv_norm
|
|
|
|
return target_vector + unspecified_vector
|
|
|
|
|
|
|
|
def set_to_vector(self, vect):
|
|
|
|
assert len(vect) == len(self.sliders)
|
|
|
|
for slider, value in zip(self.sliders, vect):
|
|
|
|
slider.set_value(value)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
|
|
|
def get_vector(self):
|
|
|
|
return np.array([slider.get_value() for slider in self.sliders])
|
|
|
|
|
|
|
|
def get_center_point(self):
|
|
|
|
return np.array([slider.center_value for slider in self.sliders])
|
|
|
|
|
|
|
|
def get_current_total_real_estate(self):
|
|
|
|
return sum([
|
|
|
|
slider.get_real_estate()
|
|
|
|
for slider in self.sliders
|
|
|
|
])
|
|
|
|
|
|
|
|
def get_all_dial_supplement_animations(self):
|
|
|
|
return [
|
|
|
|
slider.get_dial_supplement_animation()
|
|
|
|
for slider in self.sliders
|
|
|
|
]
|
|
|
|
|
|
|
|
def initialize_ambiant_slider_movement(self):
|
|
|
|
self.ambiently_change_sliders = True
|
|
|
|
self.ambient_change_end_time = np.inf
|
|
|
|
self.ambient_change_time = 0
|
|
|
|
self.ambient_velocity, self.ambient_acceleration, self.ambient_jerk = [
|
|
|
|
self.get_random_vector(magnitude)
|
|
|
|
for magnitude in [
|
|
|
|
self.ambient_velocity_magnitude,
|
|
|
|
self.ambient_acceleration_magnitude,
|
|
|
|
self.ambient_jerk_magnitude,
|
|
|
|
]
|
|
|
|
]
|
2017-08-07 23:51:58 -07:00
|
|
|
##Ensure counterclockwise rotations in 2D
|
|
|
|
if len(self.ambient_velocity) == 2:
|
|
|
|
cross = np.cross(self.get_vector(), self.ambient_velocity)
|
|
|
|
if cross < 0:
|
|
|
|
self.ambient_velocity *= -1
|
2017-08-07 16:17:35 -07:00
|
|
|
self.add_foreground_mobjects(self.sliders)
|
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
def wind_down_ambient_movement(self, time = 1, dither = True):
|
2017-08-07 16:17:35 -07:00
|
|
|
self.ambient_change_end_time = self.ambient_change_time + time
|
2017-08-07 23:51:58 -07:00
|
|
|
if dither:
|
|
|
|
self.dither(time)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
|
|
|
def ambient_slider_movement_update(self):
|
|
|
|
#Set velocity_magnitude based on start up or wind down
|
|
|
|
velocity_magnitude = float(self.ambient_velocity_magnitude)
|
|
|
|
if self.ambient_change_time <= 1:
|
|
|
|
velocity_magnitude *= smooth(self.ambient_change_time)
|
|
|
|
time_until_end = self.ambient_change_end_time - self.ambient_change_time
|
|
|
|
if time_until_end <= 1:
|
|
|
|
velocity_magnitude *= smooth(time_until_end)
|
2017-08-07 23:51:58 -07:00
|
|
|
if time_until_end < 0:
|
2017-08-07 16:17:35 -07:00
|
|
|
self.ambiently_change_sliders = False
|
|
|
|
return
|
|
|
|
|
|
|
|
center_point = self.get_center_point()
|
|
|
|
target_vector = self.get_vector() - center_point
|
|
|
|
vectors_and_magnitudes = [
|
|
|
|
(self.ambient_acceleration, self.ambient_acceleration_magnitude),
|
|
|
|
(self.ambient_velocity, velocity_magnitude),
|
|
|
|
(target_vector, np.sqrt(self.total_real_estate)),
|
|
|
|
]
|
|
|
|
jerk = self.get_random_vector(self.ambient_jerk_magnitude)
|
|
|
|
deriv = jerk
|
|
|
|
for vect, mag in vectors_and_magnitudes:
|
|
|
|
vect += self.frame_duration*deriv
|
|
|
|
if vect is self.ambient_velocity:
|
|
|
|
unit_r_vect = target_vector / np.linalg.norm(target_vector)
|
|
|
|
vect -= np.dot(vect, unit_r_vect)*unit_r_vect
|
|
|
|
vect *= mag/np.linalg.norm(vect)
|
|
|
|
deriv = vect
|
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
self.set_to_vector(target_vector + center_point)
|
2017-08-07 16:17:35 -07:00
|
|
|
self.ambient_change_time += self.frame_duration
|
|
|
|
|
|
|
|
def get_random_vector(self, magnitude):
|
|
|
|
result = 2*np.random.random(len(self.sliders)) - 1
|
|
|
|
result *= magnitude / np.linalg.norm(result)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def update_frame(self, *args, **kwargs):
|
|
|
|
if self.ambiently_change_sliders:
|
|
|
|
self.ambient_slider_movement_update()
|
|
|
|
Scene.update_frame(self, *args, **kwargs)
|
|
|
|
|
|
|
|
def dither(self, time = 1):
|
|
|
|
if self.ambiently_change_sliders:
|
|
|
|
self.play(Animation(self.sliders, run_time = time))
|
|
|
|
else:
|
2017-08-07 17:16:31 -07:00
|
|
|
Scene.dither(self,time)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
|
|
|
##########
|
|
|
|
|
2017-08-05 20:47:06 -07:00
|
|
|
class MathIsATease(Scene):
|
|
|
|
def construct(self):
|
|
|
|
randy = Randolph()
|
|
|
|
lashes = VGroup()
|
|
|
|
for eye in randy.eyes:
|
|
|
|
for angle in np.linspace(-np.pi/3, np.pi/3, 12):
|
|
|
|
lash = Line(ORIGIN, RIGHT)
|
|
|
|
lash.set_stroke(DARK_GREY, 2)
|
|
|
|
lash.scale_to_fit_width(0.27)
|
|
|
|
lash.next_to(ORIGIN, RIGHT, buff = 0)
|
|
|
|
lash.rotate(angle + np.pi/2)
|
|
|
|
lash.shift(eye.get_center())
|
|
|
|
lashes.add(lash)
|
|
|
|
lashes.do_in_place(lashes.stretch, 0.8, 1)
|
|
|
|
lashes.shift(0.04*DOWN)
|
|
|
|
|
|
|
|
|
|
|
|
fan = SVGMobject(
|
|
|
|
file_name = "fan",
|
|
|
|
fill_opacity = 1,
|
|
|
|
fill_color = YELLOW,
|
|
|
|
stroke_width = 2,
|
|
|
|
stroke_color = YELLOW,
|
|
|
|
height = 0.7,
|
|
|
|
)
|
|
|
|
VGroup(*fan[-12:]).set_fill(YELLOW_E)
|
|
|
|
fan.rotate(-np.pi/4)
|
|
|
|
fan.move_to(randy)
|
|
|
|
fan.shift(0.85*UP+0.25*LEFT)
|
|
|
|
|
|
|
|
self.add(randy)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(lashes, submobject_mode = "all_at_once"),
|
|
|
|
randy.change, "tease",
|
|
|
|
randy.look, OUT,
|
|
|
|
)
|
|
|
|
self.add_foreground_mobjects(fan)
|
|
|
|
eye_bottom_y = randy.eyes.get_bottom()[1]
|
|
|
|
self.play(
|
|
|
|
ApplyMethod(
|
|
|
|
lashes.apply_function,
|
|
|
|
lambda p : [p[0], eye_bottom_y, p[2]],
|
|
|
|
rate_func = Blink.CONFIG["rate_func"],
|
|
|
|
),
|
|
|
|
Blink(randy),
|
|
|
|
DrawBorderThenFill(fan),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
ApplyMethod(
|
|
|
|
lashes.apply_function,
|
|
|
|
lambda p : [p[0], eye_bottom_y, p[2]],
|
|
|
|
rate_func = Blink.CONFIG["rate_func"],
|
|
|
|
),
|
|
|
|
Blink(randy),
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class TODODeterminants(TODOStub):
|
|
|
|
CONFIG = {
|
|
|
|
"message" : "Determinants clip"
|
|
|
|
}
|
|
|
|
|
|
|
|
class CircleToPairsOfPoints(Scene):
|
|
|
|
def construct(self):
|
|
|
|
plane = NumberPlane(written_coordinate_height = 0.3)
|
|
|
|
plane.scale(2)
|
|
|
|
plane.add_coordinates(y_vals = [-1, 1])
|
|
|
|
background_plane = plane.copy()
|
|
|
|
background_plane.highlight(GREY)
|
|
|
|
background_plane.fade()
|
|
|
|
circle = Circle(radius = 2, color = YELLOW)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
|
|
|
x, y = [np.sqrt(2)/2]*2
|
|
|
|
dot = Dot(2*x*RIGHT + 2*y*UP, color = LIGHT_GREY)
|
2017-08-05 20:47:06 -07:00
|
|
|
|
|
|
|
equation = TexMobject("x", "^2", "+", "y", "^2", "=", "1")
|
|
|
|
equation.highlight_by_tex("x", GREEN)
|
|
|
|
equation.highlight_by_tex("y", RED)
|
|
|
|
equation.to_corner(UP+LEFT)
|
|
|
|
equation.add_background_rectangle()
|
|
|
|
|
|
|
|
coord_pair = TexMobject("(", "-%.02f"%x, ",", "-%.02f"%y, ")")
|
2017-08-07 16:17:35 -07:00
|
|
|
fixed_numbers = coord_pair.get_parts_by_tex("-")
|
2017-08-05 20:47:06 -07:00
|
|
|
fixed_numbers.set_fill(opacity = 0)
|
|
|
|
coord_pair.add_background_rectangle()
|
|
|
|
coord_pair.next_to(dot, UP+RIGHT, SMALL_BUFF)
|
|
|
|
numbers = VGroup(*[
|
|
|
|
DecimalNumber(val).replace(num, dim_to_match = 1)
|
|
|
|
for val, num in zip([x, y], fixed_numbers)
|
|
|
|
])
|
|
|
|
numbers[0].highlight(GREEN)
|
|
|
|
numbers[1].highlight(RED)
|
|
|
|
|
|
|
|
def get_update_func(i):
|
|
|
|
return lambda t : dot.get_center()[i]/2.0
|
|
|
|
|
|
|
|
|
|
|
|
self.add(background_plane, plane)
|
|
|
|
self.play(ShowCreation(circle))
|
|
|
|
self.play(
|
2017-08-07 16:17:35 -07:00
|
|
|
FadeIn(coord_pair),
|
|
|
|
Write(numbers, run_time = 1),
|
2017-08-05 20:47:06 -07:00
|
|
|
ShowCreation(dot),
|
2017-08-07 16:17:35 -07:00
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Write(equation),
|
2017-08-05 20:47:06 -07:00
|
|
|
*[
|
2017-08-07 16:17:35 -07:00
|
|
|
Transform(
|
|
|
|
number.copy(),
|
|
|
|
equation.get_parts_by_tex(tex),
|
|
|
|
remover = True
|
2017-08-05 20:47:06 -07:00
|
|
|
)
|
|
|
|
for tex, number in zip("xy", numbers)
|
|
|
|
]
|
|
|
|
)
|
2017-08-07 16:17:35 -07:00
|
|
|
self.play(FocusOn(dot, run_time = 1))
|
2017-08-05 20:47:06 -07:00
|
|
|
self.play(
|
|
|
|
Rotating(
|
|
|
|
dot, run_time = 7, in_place = False,
|
|
|
|
rate_func = smooth,
|
|
|
|
),
|
|
|
|
MaintainPositionRelativeTo(coord_pair, dot),
|
|
|
|
*[
|
|
|
|
ChangingDecimal(
|
|
|
|
num, get_update_func(i),
|
|
|
|
tracked_mobject = fixed_num
|
|
|
|
)
|
|
|
|
for num, i, fixed_num in zip(
|
|
|
|
numbers, (0, 1), fixed_numbers
|
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
######### Rotation equations ##########
|
|
|
|
|
|
|
|
rot_equation = TexMobject(
|
|
|
|
"\\Rightarrow"
|
|
|
|
"\\big(\\cos(\\theta)x - \\sin(\\theta)y\\big)^2 + ",
|
|
|
|
"\\big(\\sin(\\theta)x + \\cos(\\theta)y\\big)^2 = 1",
|
|
|
|
)
|
|
|
|
rot_equation.scale(0.9)
|
|
|
|
rot_equation.next_to(equation, RIGHT)
|
|
|
|
rot_equation.add_background_rectangle()
|
|
|
|
|
|
|
|
words = TextMobject("Rotational \\\\ symmetry")
|
|
|
|
words.next_to(ORIGIN, UP)
|
|
|
|
words.to_edge(RIGHT)
|
|
|
|
words.add_background_rectangle()
|
|
|
|
|
|
|
|
arrow = Arrow(
|
|
|
|
words.get_left(), rot_equation.get_bottom(),
|
|
|
|
path_arc = -np.pi/6
|
|
|
|
)
|
2017-08-07 16:17:35 -07:00
|
|
|
randy = Randolph(color = GREY_BROWN)
|
|
|
|
randy.to_corner(DOWN+LEFT)
|
2017-08-05 20:47:06 -07:00
|
|
|
|
|
|
|
self.play(
|
|
|
|
Write(rot_equation, run_time = 2),
|
|
|
|
FadeOut(coord_pair),
|
|
|
|
FadeOut(numbers),
|
|
|
|
FadeOut(dot),
|
|
|
|
FadeIn(randy)
|
|
|
|
)
|
|
|
|
self.play(randy.change, "confused", rot_equation)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.play(
|
|
|
|
Write(words, run_time = 1),
|
|
|
|
ShowCreation(arrow),
|
|
|
|
randy.look_at, words
|
|
|
|
)
|
|
|
|
plane.remove(*plane.coordinate_labels)
|
|
|
|
self.play(
|
|
|
|
Rotate(
|
|
|
|
plane, np.pi/3,
|
|
|
|
run_time = 4,
|
|
|
|
rate_func = there_and_back
|
|
|
|
),
|
|
|
|
Animation(equation),
|
|
|
|
Animation(rot_equation),
|
|
|
|
Animation(words),
|
|
|
|
Animation(arrow),
|
|
|
|
Animation(circle),
|
|
|
|
randy.change, "hooray"
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
2017-08-07 16:17:35 -07:00
|
|
|
class GreatSourceOfMaterial(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
self.teacher_says(
|
|
|
|
"It's a great source \\\\ of material.",
|
|
|
|
target_mode = "hooray"
|
|
|
|
)
|
|
|
|
self.change_student_modes(*["happy"]*3)
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
class CirclesSpheresSumsSquares(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class BackAndForth(Scene):
|
|
|
|
def construct(self):
|
|
|
|
analytic = TextMobject("Analytic")
|
|
|
|
analytic.shift(SPACE_WIDTH*LEFT/2)
|
|
|
|
analytic.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
|
|
geometric = TextMobject("Geometric")
|
|
|
|
geometric.shift(SPACE_WIDTH*RIGHT/2)
|
|
|
|
geometric.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
|
|
h_line = Line(LEFT, RIGHT).scale(SPACE_WIDTH)
|
|
|
|
h_line.to_edge(UP, LARGE_BUFF)
|
|
|
|
v_line = Line(UP, DOWN).scale(SPACE_HEIGHT)
|
|
|
|
self.add(analytic, geometric, h_line, v_line)
|
|
|
|
|
|
|
|
pair = TexMobject("(", "x", ",", "y", ")")
|
|
|
|
pair.shift(SPACE_WIDTH*LEFT/2 + SPACE_HEIGHT*UP/3)
|
|
|
|
triplet = TexMobject("(", "x", ",", "y", ",", "z", ")")
|
|
|
|
triplet.shift(SPACE_WIDTH*LEFT/2 + SPACE_HEIGHT*DOWN/2)
|
|
|
|
for mob in pair, triplet:
|
|
|
|
arrow = DoubleArrow(LEFT, RIGHT)
|
|
|
|
arrow.move_to(mob)
|
|
|
|
arrow.shift(2*RIGHT)
|
|
|
|
mob.arrow = arrow
|
|
|
|
circle_eq = TexMobject("x", "^2", "+", "y", "^2", "=", "1")
|
|
|
|
circle_eq.move_to(pair)
|
|
|
|
sphere_eq = TexMobject("x", "^2", "+", "y", "^2", "+", "z", "^2", "=", "1")
|
|
|
|
sphere_eq.move_to(triplet)
|
|
|
|
|
|
|
|
plane = NumberPlane(x_unit_size = 2, y_unit_size = 2)
|
|
|
|
circle = Circle(radius = 2, color = YELLOW)
|
|
|
|
plane_group = VGroup(plane, circle)
|
|
|
|
plane_group.scale(0.4)
|
|
|
|
plane_group.next_to(h_line, DOWN, SMALL_BUFF)
|
|
|
|
plane_group.shift(SPACE_WIDTH*RIGHT/2)
|
|
|
|
|
|
|
|
|
|
|
|
self.play(Write(pair))
|
|
|
|
# self.play(ShowCreation(pair.arrow))
|
|
|
|
self.play(ShowCreation(plane, run_time = 3))
|
|
|
|
self.play(Write(triplet))
|
|
|
|
# self.play(ShowCreation(triplet.arrow))
|
|
|
|
self.dither(3)
|
|
|
|
for tup, eq, to_draw in (pair, circle_eq, circle), (triplet, sphere_eq, VMobject()):
|
|
|
|
for mob in tup, eq:
|
|
|
|
mob.xyz = VGroup(*filter(
|
|
|
|
lambda sm : sm is not None,
|
|
|
|
map(mob.get_part_by_tex, "xyz")
|
|
|
|
))
|
|
|
|
self.play(
|
|
|
|
ReplacementTransform(tup.xyz, eq.xyz),
|
|
|
|
FadeOut(VGroup(*filter(
|
|
|
|
lambda sm : sm not in tup.xyz, tup
|
|
|
|
))),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Write(VGroup(*filter(
|
|
|
|
lambda sm : sm not in eq.xyz, eq
|
|
|
|
))),
|
|
|
|
ShowCreation(to_draw)
|
|
|
|
)
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
class SphereForming(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class PreviousVideos(Scene):
|
|
|
|
def construct(self):
|
|
|
|
titles = VGroup(*map(TextMobject, [
|
|
|
|
"Pi hiding in prime regularities",
|
|
|
|
"Visualizing all possible pythagorean triples",
|
|
|
|
"Borsuk-Ulam theorem",
|
|
|
|
]))
|
|
|
|
titles.to_edge(UP, buff = MED_SMALL_BUFF)
|
|
|
|
screen = ScreenRectangle(height = 6)
|
|
|
|
screen.next_to(titles, DOWN)
|
|
|
|
|
|
|
|
title = titles[0]
|
|
|
|
self.add(title, screen)
|
|
|
|
self.dither(2)
|
|
|
|
for new_title in titles[1:]:
|
|
|
|
self.play(Transform(title, new_title))
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
class TODOTease(TODOStub):
|
|
|
|
CONFIG = {
|
|
|
|
"message" : "Tease"
|
|
|
|
}
|
|
|
|
|
|
|
|
class AskAboutLongerLists(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
question = TextMobject(
|
|
|
|
"What about \\\\",
|
|
|
|
"$(x_1, x_2, x_3, x_4)?$"
|
|
|
|
)
|
|
|
|
tup = question[1]
|
|
|
|
alt_tups = map(TextMobject, [
|
|
|
|
"$(x_1, x_2, x_3, x_4, x_5)?$",
|
|
|
|
"$(x_1, x_2, \\dots, x_{99}, x_{100})?$"
|
|
|
|
])
|
|
|
|
|
|
|
|
self.student_says(question, run_time = 1)
|
|
|
|
self.dither()
|
|
|
|
for alt_tup in alt_tups:
|
|
|
|
alt_tup.move_to(tup)
|
|
|
|
self.play(Transform(tup, alt_tup))
|
|
|
|
self.dither()
|
|
|
|
self.dither()
|
|
|
|
self.play(
|
|
|
|
RemovePiCreatureBubble(self.students[1]),
|
|
|
|
self.teacher.change, "raise_right_hand"
|
|
|
|
)
|
|
|
|
self.change_student_modes(
|
|
|
|
*["confused"]*3,
|
|
|
|
look_at_arg = self.teacher.get_top() + 2*UP
|
|
|
|
)
|
|
|
|
self.play(self.teacher.look, UP)
|
|
|
|
self.dither(5)
|
|
|
|
self.student_says(
|
|
|
|
"I...don't see it.",
|
|
|
|
target_mode = "maybe",
|
|
|
|
student_index = 0
|
|
|
|
)
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
class FourDCubeRotation(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class HypersphereRotation(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class FourDSurfaceRotating(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class Professionals(PiCreatureScene):
|
|
|
|
def construct(self):
|
|
|
|
self.introduce_characters()
|
|
|
|
self.add_equation()
|
|
|
|
self.analogies()
|
|
|
|
|
|
|
|
def add_equation(self):
|
|
|
|
quaternion = TexMobject(
|
|
|
|
"\\frac{1}{2}", "+",
|
|
|
|
"0", "\\textbf{i}", "+",
|
|
|
|
"\\frac{\\sqrt{6}}{4}", "\\textbf{j}", "+",
|
|
|
|
"\\frac{\\sqrt{6}}{4}", "\\textbf{k}",
|
|
|
|
)
|
|
|
|
quaternion.shift(SPACE_WIDTH*LEFT/2)
|
|
|
|
equation = TexMobject(
|
|
|
|
"\\textbf{i}", "^2", "=",
|
|
|
|
"\\textbf{j}", "^2", "=",
|
|
|
|
"\\textbf{k}", "^2", "=",
|
|
|
|
"\\textbf{i}", "\\textbf{j}", "\\textbf{k}", "=",
|
|
|
|
"-1"
|
|
|
|
)
|
|
|
|
equation.shift(SPACE_WIDTH*RIGHT/2)
|
|
|
|
VGroup(quaternion, equation).to_edge(UP)
|
|
|
|
for mob in quaternion, equation:
|
|
|
|
mob.highlight_by_tex_to_color_map({
|
|
|
|
"i" : RED,
|
|
|
|
"j" : GREEN,
|
|
|
|
"k" : BLUE,
|
|
|
|
})
|
|
|
|
|
|
|
|
brace = Brace(quaternion, DOWN)
|
|
|
|
words = brace.get_text("4 numbers")
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
Write(quaternion),
|
|
|
|
Write(equation),
|
|
|
|
GrowFromCenter(brace),
|
|
|
|
FadeIn(words),
|
|
|
|
run_time = 2
|
|
|
|
)
|
|
|
|
self.play(*[
|
|
|
|
ApplyMethod(pi.change, "pondering", quaternion)
|
|
|
|
for pi in self.pi_creatures
|
|
|
|
])
|
|
|
|
self.dither()
|
|
|
|
self.play(FadeOut(VGroup(brace, words)))
|
|
|
|
|
|
|
|
|
|
|
|
self.quaternion = quaternion
|
|
|
|
self.equation = equation
|
|
|
|
|
|
|
|
def introduce_characters(self):
|
|
|
|
titles = VGroup(*map(TextMobject, [
|
|
|
|
"Mathematician",
|
|
|
|
"Computer scientist",
|
|
|
|
"Physicist",
|
|
|
|
]))
|
|
|
|
self.remove(*self.pi_creatures)
|
|
|
|
for title, pi in zip(titles, self.pi_creatures):
|
|
|
|
title.next_to(pi, DOWN)
|
|
|
|
self.play(
|
|
|
|
Animation(VectorizedPoint(pi.eyes.get_center())),
|
|
|
|
FadeIn(pi),
|
|
|
|
Write(title, run_time = 1),
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
def analogies(self):
|
|
|
|
examples = VGroup()
|
|
|
|
plane = ComplexPlane(
|
|
|
|
x_radius = 2.5,
|
|
|
|
y_radius = 1.5,
|
|
|
|
)
|
|
|
|
plane.add_coordinates()
|
|
|
|
plane.add(Circle(color = YELLOW))
|
|
|
|
plane.scale(0.75)
|
|
|
|
examples.add(plane)
|
|
|
|
examples.add(Circle())
|
|
|
|
examples.arrange_submobjects(RIGHT, buff = 2)
|
|
|
|
examples.next_to(self.pi_creatures, UP, MED_LARGE_BUFF)
|
|
|
|
labels = VGroup(*map(TextMobject, ["2D", "3D"]))
|
|
|
|
|
|
|
|
title = TextMobject("Fly by instruments")
|
|
|
|
title.scale(1.5)
|
|
|
|
title.to_edge(UP)
|
|
|
|
|
|
|
|
for label, example in zip(labels, examples):
|
|
|
|
label.next_to(example, DOWN)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(example),
|
|
|
|
Write(label, run_time = 1)
|
|
|
|
)
|
|
|
|
example.add(label)
|
|
|
|
self.dither()
|
|
|
|
self.dither()
|
|
|
|
self.play(
|
|
|
|
FadeOut(examples),
|
|
|
|
VGroup(self.quaternion, self.equation).shift, 2*DOWN,
|
|
|
|
Write(title, run_time = 2)
|
|
|
|
)
|
|
|
|
self.play(*[
|
|
|
|
ApplyMethod(
|
|
|
|
pi.change, mode, self.equation,
|
|
|
|
run_time = 2,
|
|
|
|
rate_func = squish_rate_func(smooth, a, a+0.5)
|
|
|
|
)
|
|
|
|
for pi, mode, a in zip(
|
|
|
|
self.pi_creatures,
|
|
|
|
["confused", "sassy", "erm"],
|
|
|
|
np.linspace(0, 0.5, len(self.pi_creatures))
|
|
|
|
)
|
|
|
|
])
|
|
|
|
self.dither()
|
|
|
|
self.play(Animation(self.quaternion))
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
######
|
|
|
|
|
|
|
|
def create_pi_creatures(self):
|
|
|
|
mathy = Mathematician()
|
|
|
|
physy = PiCreature(color = PINK)
|
|
|
|
compy = PiCreature(color = PURPLE)
|
|
|
|
pi_creatures = VGroup(mathy, compy, physy)
|
|
|
|
for pi in pi_creatures:
|
|
|
|
pi.scale(0.7)
|
|
|
|
pi_creatures.arrange_submobjects(RIGHT, buff = 3)
|
|
|
|
pi_creatures.to_edge(DOWN, buff = LARGE_BUFF)
|
|
|
|
return pi_creatures
|
|
|
|
|
|
|
|
class OfferAHybrid(SliderScene):
|
2017-08-07 17:16:31 -07:00
|
|
|
CONFIG = {
|
|
|
|
"n_sliders" : 3,
|
|
|
|
}
|
2017-08-07 16:17:35 -07:00
|
|
|
def construct(self):
|
2017-08-07 17:16:31 -07:00
|
|
|
self.remove(self.sliders)
|
|
|
|
titles = self.get_titles()
|
|
|
|
h_line = Line(LEFT, RIGHT).scale(SPACE_WIDTH)
|
|
|
|
h_line.next_to(titles, DOWN)
|
|
|
|
v_lines = VGroup(*[
|
|
|
|
Line(UP, DOWN).scale(SPACE_HEIGHT)
|
|
|
|
for x in range(2)
|
|
|
|
])
|
|
|
|
v_lines.generate_target()
|
|
|
|
for line, vect in zip(v_lines.target, [LEFT, RIGHT]):
|
|
|
|
line.shift(vect*SPACE_WIDTH/3)
|
|
|
|
|
|
|
|
equation = TexMobject("x^2 + y^2 + z^2 = 1")
|
|
|
|
equation.generate_target()
|
|
|
|
equation.shift(SPACE_WIDTH*LEFT/2)
|
|
|
|
equation.target.shift(2*SPACE_WIDTH*LEFT/3)
|
|
|
|
|
|
|
|
self.add(titles, h_line, v_lines, equation)
|
|
|
|
self.dither()
|
|
|
|
self.play(*map(MoveToTarget, [titles, v_lines, equation]))
|
|
|
|
self.play(Write(self.sliders, run_time = 1))
|
2017-08-07 16:17:35 -07:00
|
|
|
self.initialize_ambiant_slider_movement()
|
2017-08-07 17:16:31 -07:00
|
|
|
self.dither(10)
|
2017-08-07 16:17:35 -07:00
|
|
|
self.wind_down_ambient_movement()
|
2017-08-07 23:51:58 -07:00
|
|
|
self.dither()
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 17:16:31 -07:00
|
|
|
def get_titles(self):
|
|
|
|
titles = VGroup(*map(TextMobject, [
|
|
|
|
"Analytic", "Hybrid", "Geometric"
|
|
|
|
]))
|
|
|
|
titles.to_edge(UP)
|
|
|
|
titles[1].highlight(BLUE)
|
|
|
|
titles.generate_target()
|
|
|
|
titles[1].scale_in_place(0.001)
|
|
|
|
titles[0].shift(SPACE_WIDTH*LEFT/2)
|
|
|
|
titles.target[0].shift(2*SPACE_WIDTH*LEFT/3)
|
|
|
|
titles[2].shift(SPACE_WIDTH*RIGHT/2)
|
|
|
|
titles.target[2].shift(2*SPACE_WIDTH*RIGHT/3)
|
|
|
|
return titles
|
|
|
|
|
|
|
|
class RotatingSphereWithWanderingPoint(ExternallyAnimatedScene):
|
|
|
|
pass
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
class DismissProjection(PiCreatureScene):
|
|
|
|
CONFIG = {
|
|
|
|
"screen_rect_color" : WHITE,
|
|
|
|
"example_vect" : np.array([0.52, 0.26, 0.53, 0.60]),
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.remove(self.pi_creature)
|
|
|
|
self.show_all_spheres()
|
|
|
|
self.discuss_4d_sphere_definition()
|
|
|
|
self.talk_through_animation()
|
|
|
|
self.transition_to_next_scene()
|
|
|
|
|
|
|
|
def show_all_spheres(self):
|
|
|
|
equations = VGroup(*map(TexMobject, [
|
|
|
|
"x^2 + y^2 = 1",
|
|
|
|
"x^2 + y^2 + z^2 = 1",
|
|
|
|
"x^2 + y^2 + z^2 + w^2 = 1",
|
|
|
|
]))
|
|
|
|
colors = [YELLOW, GREEN, BLUE]
|
|
|
|
for equation, edge, color in zip(equations, [LEFT, ORIGIN, RIGHT], colors):
|
|
|
|
equation.highlight(color)
|
|
|
|
equation.shift(3*UP)
|
|
|
|
equation.to_edge(edge)
|
|
|
|
equations[1].shift(LEFT)
|
|
|
|
|
|
|
|
spheres = VGroup(
|
|
|
|
self.get_circle(equations[0]),
|
|
|
|
self.get_sphere_screen(equations[1], DOWN),
|
|
|
|
self.get_sphere_screen(equations[2], DOWN),
|
|
|
|
)
|
|
|
|
|
|
|
|
for equation, sphere in zip(equations, spheres):
|
|
|
|
self.play(
|
|
|
|
Write(equation),
|
|
|
|
LaggedStart(ShowCreation, sphere),
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
self.equations = equations
|
|
|
|
self.spheres = spheres
|
|
|
|
|
|
|
|
def get_circle(self, equation):
|
|
|
|
result = VGroup(
|
|
|
|
NumberPlane(
|
|
|
|
x_radius = 2.5,
|
|
|
|
y_radius = 2,
|
|
|
|
).fade(0.4),
|
|
|
|
Circle(color = YELLOW, radius = 1),
|
|
|
|
)
|
|
|
|
result.scale(0.7)
|
|
|
|
result.next_to(equation, DOWN)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def get_sphere_screen(self, equation, vect):
|
|
|
|
square = Rectangle()
|
|
|
|
square.scale_to_fit_width(equation.get_width())
|
|
|
|
square.stretch_to_fit_height(3)
|
|
|
|
square.next_to(equation, vect)
|
|
|
|
square.highlight(self.screen_rect_color)
|
|
|
|
return square
|
|
|
|
|
|
|
|
def discuss_4d_sphere_definition(self):
|
|
|
|
sphere = self.spheres[-1]
|
|
|
|
equation = self.equations[-1]
|
|
|
|
|
|
|
|
sphere_words = TextMobject("``4-dimensional sphere''")
|
|
|
|
sphere_words.next_to(sphere, DOWN+LEFT, buff = LARGE_BUFF)
|
|
|
|
arrow = Arrow(
|
|
|
|
sphere_words.get_right(), sphere.get_bottom(),
|
|
|
|
path_arc = np.pi/3,
|
|
|
|
color = BLUE
|
|
|
|
)
|
|
|
|
descriptor = TexMobject(
|
|
|
|
"\\text{Just lists of numbers like }",
|
|
|
|
"(%.02f \\,, %.02f \\,, %.02f \\,, %.02f \\,)"%tuple(self.example_vect)
|
|
|
|
)
|
|
|
|
descriptor[1].highlight(BLUE)
|
|
|
|
descriptor.next_to(sphere_words, DOWN)
|
|
|
|
dot = Dot(descriptor[1].get_top())
|
|
|
|
dot.set_fill(WHITE, opacity = 0.75)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
Write(sphere_words),
|
|
|
|
ShowCreation(
|
|
|
|
arrow,
|
|
|
|
rate_func = squish_rate_func(smooth, 0.5, 1)
|
|
|
|
),
|
|
|
|
run_time = 3,
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
self.play(Write(descriptor, run_time = 2))
|
|
|
|
self.dither()
|
|
|
|
self.play(
|
|
|
|
dot.move_to, equation.get_left(),
|
|
|
|
dot.set_fill, None, 0,
|
|
|
|
path_arc = -np.pi/12
|
|
|
|
)
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
self.sphere_words = sphere_words
|
|
|
|
self.sphere_arrow = arrow
|
|
|
|
self.descriptor = descriptor
|
|
|
|
|
|
|
|
def talk_through_animation(self):
|
|
|
|
sphere = self.spheres[-1]
|
|
|
|
|
|
|
|
morty = self.pi_creature
|
|
|
|
alt_dims = VGroup(*map(TextMobject, ["5D", "6D", "7D"]))
|
|
|
|
alt_dims.next_to(morty.eyes, UP, SMALL_BUFF)
|
|
|
|
alt_dim = alt_dims[0]
|
|
|
|
|
|
|
|
self.play(FadeIn(morty))
|
|
|
|
self.play(morty.change, "raise_right_hand", sphere)
|
|
|
|
self.dither(3)
|
|
|
|
self.play(morty.change, "confused", sphere)
|
|
|
|
self.dither(3)
|
|
|
|
self.play(
|
|
|
|
morty.change, "erm", alt_dims,
|
|
|
|
FadeIn(alt_dim)
|
|
|
|
)
|
|
|
|
for new_alt_dim in alt_dims[1:]:
|
|
|
|
self.dither()
|
|
|
|
self.play(Transform(alt_dim, new_alt_dim))
|
|
|
|
self.dither()
|
|
|
|
self.play(morty.change, "concerned_musician")
|
|
|
|
self.play(FadeOut(alt_dim))
|
|
|
|
self.dither()
|
|
|
|
self.play(morty.change, "angry", sphere)
|
|
|
|
self.dither(2)
|
|
|
|
|
|
|
|
def transition_to_next_scene(self):
|
|
|
|
equation = self.equations[-1]
|
|
|
|
self.equations.remove(equation)
|
|
|
|
tup = self.descriptor[1]
|
|
|
|
self.descriptor.remove(tup)
|
|
|
|
|
|
|
|
equation.generate_target()
|
|
|
|
equation.target.center().to_edge(UP)
|
|
|
|
tup.generate_target()
|
|
|
|
tup.target.next_to(equation.target, DOWN)
|
|
|
|
tup.target.highlight(WHITE)
|
|
|
|
|
|
|
|
self.play(LaggedStart(FadeOut, VGroup(*[
|
|
|
|
self.equations, self.spheres,
|
|
|
|
self.sphere_words, self.sphere_arrow,
|
|
|
|
self.descriptor,
|
|
|
|
self.pi_creature
|
|
|
|
])))
|
|
|
|
self.play(*map(MoveToTarget, [equation, tup]))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
def create_pi_creature(self):
|
|
|
|
return Mortimer().scale(0.8).to_corner(DOWN+RIGHT)
|
|
|
|
|
|
|
|
class RotatingSphere(ExternallyAnimatedScene):
|
|
|
|
pass
|
|
|
|
|
|
|
|
class Introduce4DSliders(SliderScene):
|
|
|
|
CONFIG = {
|
|
|
|
"slider_config" : {
|
|
|
|
"include_real_estate_ticks" : False,
|
|
|
|
"numbers_with_elongated_ticks" : [-1, 0, 1],
|
|
|
|
"tick_frequency" : 0.25,
|
|
|
|
"tick_size" : 0.05,
|
|
|
|
"dial_color" : YELLOW,
|
|
|
|
},
|
|
|
|
"slider_spacing" : LARGE_BUFF,
|
|
|
|
}
|
|
|
|
def construct(self):
|
|
|
|
self.match_last_scene()
|
|
|
|
self.introduce_sliders()
|
|
|
|
self.ask_about_constraint()
|
|
|
|
|
|
|
|
def match_last_scene(self):
|
|
|
|
self.start_vect = DismissProjection.CONFIG["example_vect"]
|
|
|
|
self.remove(self.sliders)
|
|
|
|
|
|
|
|
equation = TexMobject("x^2 + y^2 + z^2 + w^2 = 1")
|
|
|
|
x, y, z, w = self.start_vect
|
|
|
|
tup = TexMobject(
|
|
|
|
"(", "%.02f \\,"%x,
|
|
|
|
",", "%.02f \\,"%y,
|
|
|
|
",", "%.02f \\,"%z,
|
|
|
|
",", "%.02f \\,"%w, ")"
|
|
|
|
)
|
|
|
|
equation.center().to_edge(UP)
|
|
|
|
equation.highlight(BLUE)
|
|
|
|
tup.next_to(equation, DOWN)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
self.sliders.next_to(tup, DOWN)
|
|
|
|
self.sliders.shift(0.8*LEFT)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
self.add(equation, tup)
|
|
|
|
self.dither()
|
|
|
|
self.equation = equation
|
|
|
|
self.tup = tup
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
def introduce_sliders(self):
|
|
|
|
self.set_to_vector(self.start_vect)
|
|
|
|
|
|
|
|
numbers = self.tup.get_parts_by_tex(".")
|
|
|
|
self.tup.remove(*numbers)
|
|
|
|
dials = VGroup(*[slider.dial for slider in self.sliders])
|
|
|
|
dial_copies = dials.copy()
|
|
|
|
dials.set_fill(opacity = 0)
|
|
|
|
|
|
|
|
self.play(LaggedStart(FadeIn, self.sliders))
|
|
|
|
self.play(*[
|
|
|
|
Transform(
|
|
|
|
num, dial,
|
|
|
|
run_time = 3,
|
|
|
|
rate_func = squish_rate_func(smooth, a, a+0.5),
|
|
|
|
remover = True
|
|
|
|
)
|
|
|
|
for num, dial, a in zip(
|
|
|
|
numbers, dial_copies,
|
|
|
|
np.linspace(0, 0.5, len(numbers))
|
|
|
|
)
|
|
|
|
])
|
|
|
|
dials.set_fill(opacity = 1)
|
|
|
|
self.initialize_ambiant_slider_movement()
|
|
|
|
self.play(FadeOut(self.tup))
|
|
|
|
self.dither(10)
|
|
|
|
|
|
|
|
def ask_about_constraint(self):
|
|
|
|
equation = self.equation
|
|
|
|
rect = SurroundingRectangle(equation, color = GREEN)
|
|
|
|
randy = Randolph().scale(0.5)
|
|
|
|
randy.next_to(rect, DOWN+LEFT, LARGE_BUFF)
|
|
|
|
|
|
|
|
self.play(ShowCreation(rect))
|
|
|
|
self.play(FadeIn(randy))
|
|
|
|
self.play(randy.change, "pondering", rect)
|
|
|
|
self.dither()
|
|
|
|
for mob in self.sliders, rect:
|
|
|
|
self.play(randy.look_at, mob)
|
|
|
|
self.play(Blink(randy))
|
|
|
|
self.dither()
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
class TwoDimensionalCase(Introduce4DSliders):
|
|
|
|
CONFIG = {
|
|
|
|
"n_sliders" : 2,
|
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
SliderScene.setup(self)
|
|
|
|
self.sliders.shift(RIGHT)
|
|
|
|
|
|
|
|
plane = NumberPlane(
|
|
|
|
x_radius = 2.5,
|
|
|
|
y_radius = 2.5,
|
|
|
|
)
|
|
|
|
plane.fade(0.25)
|
|
|
|
plane.axes.highlight(GREY)
|
|
|
|
plane.add_coordinates()
|
|
|
|
plane.to_edge(LEFT)
|
|
|
|
origin = plane.coords_to_point(0, 0)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
circle = Circle(radius = 1, color = WHITE)
|
|
|
|
circle.move_to(origin)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
dot = Dot(color = YELLOW)
|
|
|
|
dot.move_to(plane.coords_to_point(1, 0))
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
equation = TexMobject("x^2 + y^2 = 1")
|
|
|
|
equation.to_corner(UP + RIGHT)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
self.add(plane, circle, dot, equation)
|
|
|
|
self.add_foreground_mobjects(dot)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
2017-08-07 23:51:58 -07:00
|
|
|
self.plane = plane
|
|
|
|
self.circle = circle
|
|
|
|
self.dot = dot
|
|
|
|
self.equation = equation
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
self.force_skipping()
|
|
|
|
|
|
|
|
self.let_values_wander()
|
|
|
|
self.introduce_real_estate()
|
|
|
|
self.let_values_wander(6)
|
|
|
|
self.comment_on_cheap_vs_expensive_real_estate()
|
|
|
|
self.nudge_x_from_one_example()
|
|
|
|
|
|
|
|
def let_values_wander(self, total_time = 5):
|
|
|
|
self.initialize_ambiant_slider_movement()
|
|
|
|
self.dither(total_time - 1)
|
|
|
|
self.wind_down_ambient_movement()
|
|
|
|
|
|
|
|
def introduce_real_estate(self):
|
|
|
|
x_squared_mob = VGroup(*self.equation[:2])
|
|
|
|
y_squared_mob = VGroup(*self.equation[3:5])
|
|
|
|
x_rect = SurroundingRectangle(x_squared_mob)
|
|
|
|
y_rect = SurroundingRectangle(y_squared_mob)
|
|
|
|
rects = VGroup(x_rect, y_rect)
|
|
|
|
|
|
|
|
decimals = VGroup(*[
|
|
|
|
DecimalNumber(num**2)
|
|
|
|
for num in self.get_vector()
|
|
|
|
])
|
|
|
|
decimals.arrange_submobjects(RIGHT, buff = LARGE_BUFF)
|
|
|
|
decimals.next_to(rects, DOWN, LARGE_BUFF)
|
|
|
|
|
|
|
|
real_estate_word = TextMobject("``Real estate''")
|
|
|
|
real_estate_word.next_to(decimals, DOWN, MED_LARGE_BUFF)
|
|
|
|
self.play(FadeIn(real_estate_word))
|
|
|
|
|
|
|
|
colors = GREEN, RED
|
|
|
|
arrows = VGroup()
|
|
|
|
for rect, decimal, color in zip(rects, decimals, colors):
|
|
|
|
rect.highlight(color)
|
|
|
|
decimal.highlight(color)
|
|
|
|
arrow = Arrow(
|
|
|
|
rect.get_bottom()+SMALL_BUFF*UP, decimal.get_top(),
|
|
|
|
tip_length = 0.2,
|
|
|
|
)
|
|
|
|
arrow.highlight(color)
|
|
|
|
arrows.add(arrow)
|
|
|
|
|
|
|
|
self.play(ShowCreation(rect))
|
|
|
|
self.play(
|
|
|
|
ShowCreation(arrow),
|
|
|
|
Write(decimal)
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
def create_update_func(i):
|
|
|
|
return lambda alpha : self.get_vector()[i]**2
|
|
|
|
|
|
|
|
self.add_foreground_mobjects(decimals)
|
|
|
|
self.decimals = decimals
|
|
|
|
self.decimal_update_anims = [
|
|
|
|
ChangingDecimal(decimal, create_update_func(i))
|
|
|
|
for i, decimal in enumerate(decimals)
|
|
|
|
]
|
|
|
|
self.real_estate_word = real_estate_word
|
|
|
|
|
|
|
|
def comment_on_cheap_vs_expensive_real_estate(self):
|
|
|
|
blue_rects = VGroup()
|
|
|
|
red_rects = VGroup()
|
|
|
|
for slider in self.sliders:
|
|
|
|
for x1, x2 in (-0.25, 0.25), (0.75, 1.0), (-1.0, -0.75):
|
|
|
|
p1, p2 = map(slider.number_to_point, [x1, x2])
|
|
|
|
rect = Rectangle(
|
|
|
|
stroke_width = 0,
|
|
|
|
fill_opacity = 0.5,
|
|
|
|
width = 0.25,
|
|
|
|
height = (p2-p1)[1]
|
|
|
|
)
|
|
|
|
rect.move_to((p1+p2)/2)
|
|
|
|
if np.mean([x1, x2]) == 0:
|
|
|
|
rect.highlight(BLUE)
|
|
|
|
blue_rects.add(rect)
|
|
|
|
else:
|
|
|
|
rect.highlight(RED)
|
|
|
|
red_rects.add(rect)
|
|
|
|
|
|
|
|
self.play(DrawBorderThenFill(blue_rects))
|
|
|
|
self.dither()
|
|
|
|
self.play(ReplacementTransform(blue_rects, red_rects))
|
|
|
|
self.dither()
|
|
|
|
self.play(FadeOut(red_rects))
|
|
|
|
|
|
|
|
def nudge_x_from_one_example(self):
|
|
|
|
|
|
|
|
|
|
|
|
self.revert_to_original_skipping_status()
|
|
|
|
|
|
|
|
|
|
|
|
#####
|
|
|
|
|
|
|
|
def update_frame(self, *args, **kwargs):
|
|
|
|
x, y = self.get_vector()
|
|
|
|
self.dot.move_to(self.plane.coords_to_point(x, y))
|
|
|
|
if hasattr(self, "decimals"):
|
|
|
|
for anim in self.decimal_update_anims:
|
|
|
|
anim.update(0)
|
|
|
|
SliderScene.update_frame(self, *args, **kwargs)
|
2017-08-07 16:17:35 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
2017-08-05 20:47:06 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|