2017-08-24 11:44: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 *
|
2017-08-24 23:12:25 -07:00
|
|
|
from animation.continual_animation import *
|
2017-08-24 11:44:06 -07:00
|
|
|
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 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 *
|
|
|
|
|
|
|
|
E_COLOR = BLUE
|
2017-08-25 17:56:55 -07:00
|
|
|
M_COLOR = YELLOW
|
2017-08-24 11:44:06 -07:00
|
|
|
|
|
|
|
class OscillatingVector(ContinualAnimation):
|
|
|
|
CONFIG = {
|
|
|
|
"tail" : ORIGIN,
|
|
|
|
"frequency" : 1,
|
2017-08-27 23:52:28 -07:00
|
|
|
"A_vect" : [1, 0, 0],
|
|
|
|
"phi_vect" : [0, 0, 0],
|
2017-08-24 23:12:25 -07:00
|
|
|
"vector_to_be_added_to" : None,
|
2017-08-24 11:44:06 -07:00
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
self.vector = self.mobject
|
|
|
|
|
|
|
|
def update_mobject(self, dt):
|
|
|
|
f = self.frequency
|
2017-08-24 12:38:37 -07:00
|
|
|
t = self.internal_time
|
2017-08-24 11:44:06 -07:00
|
|
|
angle = 2*np.pi*f*t
|
|
|
|
vect = np.array([
|
2017-08-27 23:52:28 -07:00
|
|
|
A*np.exp(complex(0, angle + phi))
|
|
|
|
for A, phi in zip(self.A_vect, self.phi_vect)
|
2017-08-24 11:44:06 -07:00
|
|
|
]).real
|
2017-08-24 23:12:25 -07:00
|
|
|
self.update_tail()
|
2017-08-24 11:44:06 -07:00
|
|
|
self.vector.put_start_and_end_on(self.tail, self.tail+vect)
|
|
|
|
|
2017-08-24 23:12:25 -07:00
|
|
|
def update_tail(self):
|
|
|
|
if self.vector_to_be_added_to is not None:
|
|
|
|
self.tail = self.vector_to_be_added_to.get_end()
|
|
|
|
|
|
|
|
class OscillatingVectorComponents(ContinualAnimationGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"tip_to_tail" : False,
|
|
|
|
}
|
|
|
|
def __init__(self, oscillating_vector, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
|
|
|
vx = Vector(UP, color = GREEN).fade()
|
|
|
|
vy = Vector(UP, color = RED).fade()
|
|
|
|
kwargs = {
|
|
|
|
"frequency" : oscillating_vector.frequency,
|
|
|
|
"tail" : oscillating_vector.tail,
|
|
|
|
}
|
|
|
|
ovx = OscillatingVector(
|
|
|
|
vx,
|
|
|
|
A_x = oscillating_vector.A_x,
|
|
|
|
phi_x = oscillating_vector.phi_x,
|
|
|
|
A_y = 0,
|
|
|
|
phi_y = 0,
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
ovy = OscillatingVector(
|
|
|
|
vy,
|
|
|
|
A_x = 0,
|
|
|
|
phi_x = 0,
|
|
|
|
A_y = oscillating_vector.A_y,
|
|
|
|
phi_y = oscillating_vector.phi_y,
|
|
|
|
**kwargs
|
|
|
|
)
|
|
|
|
components = [ovx, ovy]
|
|
|
|
self.vectors = VGroup(ovx.vector, ovy.vector)
|
|
|
|
if self.tip_to_tail:
|
|
|
|
ovy.vector_to_be_added_to = ovx.vector
|
|
|
|
else:
|
|
|
|
self.lines = VGroup()
|
|
|
|
for ov1, ov2 in (ovx, ovy), (ovy, ovx):
|
|
|
|
ov_line = ov1.copy()
|
|
|
|
ov_line.mobject = ov_line.vector = DashedLine(
|
|
|
|
UP, DOWN, color = ov1.vector.get_color()
|
|
|
|
)
|
|
|
|
ov_line.vector_to_be_added_to = ov2.vector
|
|
|
|
components.append(ov_line)
|
|
|
|
self.lines.add(ov_line.line)
|
|
|
|
|
|
|
|
ContinualAnimationGroup.__init__(self, *components, **kwargs)
|
|
|
|
|
|
|
|
class EMWave(ContinualAnimationGroup):
|
|
|
|
CONFIG = {
|
2017-08-25 17:56:55 -07:00
|
|
|
"wave_number" : 1,
|
2017-08-24 23:12:25 -07:00
|
|
|
"frequency" : 0.25,
|
|
|
|
"n_vectors" : 40,
|
|
|
|
"propogation_direction" : RIGHT,
|
2017-08-30 13:16:08 -07:00
|
|
|
"start_point" : SPACE_WIDTH*LEFT + DOWN + OUT,
|
2017-08-24 23:12:25 -07:00
|
|
|
"length" : 2*SPACE_WIDTH,
|
|
|
|
"amplitude" : 1,
|
|
|
|
"rotation" : 0,
|
2017-08-27 23:52:28 -07:00
|
|
|
"A_vect" : [0, 0, 1],
|
|
|
|
"phi_vect" : [0, 0, 0],
|
2017-08-30 13:16:08 -07:00
|
|
|
"requires_start_up" : False,
|
2017-08-24 23:12:25 -07:00
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
2017-08-28 10:52:49 -07:00
|
|
|
if not all(self.propogation_direction == RIGHT):
|
|
|
|
self.matrix_transform = np.dot(
|
|
|
|
z_to_vector(self.propogation_direction),
|
|
|
|
np.linalg.inv(z_to_vector(RIGHT)),
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
self.matrix_transform = None
|
2017-08-24 23:12:25 -07:00
|
|
|
|
|
|
|
vector_oscillations = []
|
|
|
|
self.E_vects = VGroup()
|
|
|
|
self.M_vects = VGroup()
|
|
|
|
|
2017-08-27 23:52:28 -07:00
|
|
|
self.A_vect = np.array(self.A_vect)/np.linalg.norm(self.A_vect)
|
|
|
|
self.A_vect *= self.amplitude
|
2017-08-24 23:12:25 -07:00
|
|
|
|
|
|
|
for alpha in np.linspace(0, 1, self.n_vectors):
|
2017-08-27 23:52:28 -07:00
|
|
|
tail = interpolate(ORIGIN, self.length*RIGHT, alpha)
|
2017-08-25 17:56:55 -07:00
|
|
|
phase = -alpha*self.length*self.wave_number
|
2017-08-27 23:52:28 -07:00
|
|
|
kwargs = {
|
|
|
|
"phi_vect" : np.array(self.phi_vect) + phase,
|
|
|
|
"frequency" : self.frequency,
|
|
|
|
"tail" : np.array(tail),
|
|
|
|
}
|
2017-08-24 23:12:25 -07:00
|
|
|
E_ov = OscillatingVector(
|
2017-08-27 23:52:28 -07:00
|
|
|
Vector(
|
|
|
|
OUT, color = E_COLOR,
|
|
|
|
normal_vector = UP,
|
|
|
|
),
|
|
|
|
A_vect = self.A_vect,
|
|
|
|
**kwargs
|
2017-08-24 23:12:25 -07:00
|
|
|
)
|
|
|
|
M_ov = OscillatingVector(
|
2017-08-27 23:52:28 -07:00
|
|
|
Vector(
|
|
|
|
UP, color = M_COLOR,
|
|
|
|
normal_vector = OUT,
|
|
|
|
),
|
|
|
|
A_vect = rotate_vector(self.A_vect, np.pi/2, RIGHT),
|
|
|
|
**kwargs
|
2017-08-24 23:12:25 -07:00
|
|
|
)
|
|
|
|
vector_oscillations += [E_ov, M_ov]
|
|
|
|
self.E_vects.add(E_ov.vector)
|
|
|
|
self.M_vects.add(M_ov.vector)
|
|
|
|
ContinualAnimationGroup.__init__(self, *vector_oscillations)
|
|
|
|
|
|
|
|
def update_mobject(self, dt):
|
2017-08-30 13:16:08 -07:00
|
|
|
if self.requires_start_up:
|
|
|
|
n_wave_lengths = self.length / (2*np.pi*self.wave_number)
|
|
|
|
prop_time = n_wave_lengths/self.frequency
|
|
|
|
middle_alpha = interpolate(
|
|
|
|
0.4, 1.4,
|
|
|
|
self.external_time / prop_time
|
|
|
|
)
|
|
|
|
new_smooth = squish_rate_func(smooth, 0.4, 0.6)
|
|
|
|
|
|
|
|
ovs = self.continual_animations
|
|
|
|
for ov, alpha in zip(ovs, np.linspace(0, 1, len(ovs))):
|
|
|
|
epsilon = 0.0001
|
|
|
|
new_amplitude = np.clip(
|
|
|
|
new_smooth(middle_alpha - alpha), epsilon, 1
|
|
|
|
)
|
|
|
|
norm = np.linalg.norm(ov.A_vect)
|
|
|
|
if norm != 0:
|
|
|
|
ov.A_vect = new_amplitude * np.array(ov.A_vect) / norm
|
|
|
|
|
2017-08-24 23:12:25 -07:00
|
|
|
ContinualAnimationGroup.update_mobject(self, dt)
|
2017-08-27 23:52:28 -07:00
|
|
|
self.mobject.rotate(self.rotation, RIGHT)
|
2017-08-28 10:52:49 -07:00
|
|
|
if self.matrix_transform:
|
|
|
|
self.mobject.apply_matrix(self.matrix_transform)
|
2017-08-24 23:12:25 -07:00
|
|
|
self.mobject.shift(self.start_point)
|
|
|
|
|
|
|
|
class WavePacket(Animation):
|
|
|
|
CONFIG = {
|
|
|
|
"EMWave_config" : {
|
|
|
|
"wave_number" : 0,
|
|
|
|
"start_point" : SPACE_WIDTH*LEFT,
|
2017-08-27 23:52:28 -07:00
|
|
|
"phi_vect" : np.ones(3)*np.pi/4,
|
2017-08-24 23:12:25 -07:00
|
|
|
},
|
2017-08-25 17:56:55 -07:00
|
|
|
"em_wave" : None,
|
2017-08-24 23:12:25 -07:00
|
|
|
"run_time" : 4,
|
|
|
|
"rate_func" : None,
|
|
|
|
"packet_width" : 6,
|
|
|
|
"include_E_vects" : True,
|
|
|
|
"include_M_vects" : True,
|
|
|
|
"filter_distance" : SPACE_WIDTH,
|
|
|
|
"get_filtered" : False,
|
2017-08-25 17:56:55 -07:00
|
|
|
"remover" : True,
|
2017-08-24 23:12:25 -07:00
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
digest_config(self, kwargs)
|
2017-08-25 17:56:55 -07:00
|
|
|
em_wave = self.em_wave
|
|
|
|
if em_wave is None:
|
|
|
|
em_wave = EMWave(**self.EMWave_config)
|
|
|
|
em_wave.update(0)
|
|
|
|
self.em_wave = em_wave
|
2017-08-24 23:12:25 -07:00
|
|
|
|
|
|
|
self.vects = VGroup()
|
|
|
|
if self.include_E_vects:
|
|
|
|
self.vects.add(*em_wave.E_vects)
|
|
|
|
if self.include_M_vects:
|
|
|
|
self.vects.add(*em_wave.M_vects)
|
|
|
|
for vect in self.vects:
|
|
|
|
vect.save_state()
|
|
|
|
|
|
|
|
u = em_wave.propogation_direction
|
|
|
|
self.wave_packet_start, self.wave_packet_end = [
|
|
|
|
em_wave.start_point - u*self.packet_width/2,
|
|
|
|
em_wave.start_point + u*(em_wave.length + self.packet_width/2)
|
|
|
|
]
|
|
|
|
Animation.__init__(self, self.vects, **kwargs)
|
|
|
|
|
|
|
|
def update_mobject(self, alpha):
|
|
|
|
packet_center = interpolate(
|
|
|
|
self.wave_packet_start,
|
|
|
|
self.wave_packet_end,
|
|
|
|
alpha
|
|
|
|
)
|
|
|
|
em_wave = self.em_wave
|
|
|
|
for vect in self.vects:
|
|
|
|
tail = vect.get_start()
|
|
|
|
distance_from_packet = np.dot(
|
|
|
|
tail - packet_center,
|
|
|
|
em_wave.propogation_direction
|
|
|
|
)
|
2017-08-25 17:56:55 -07:00
|
|
|
A = em_wave.amplitude*self.E_func(distance_from_packet)
|
2017-08-24 23:12:25 -07:00
|
|
|
distance_from_start = np.linalg.norm(tail - em_wave.start_point)
|
|
|
|
if self.get_filtered and distance_from_start > self.filter_distance:
|
|
|
|
A = 0
|
|
|
|
vect.restore()
|
|
|
|
vect.scale(A/vect.get_length(), about_point = tail)
|
|
|
|
|
2017-08-25 17:56:55 -07:00
|
|
|
def E_func(self, x):
|
2017-08-27 23:52:28 -07:00
|
|
|
return np.sin(x)*np.exp(-0.25*x*x)
|
2017-08-24 23:12:25 -07:00
|
|
|
|
|
|
|
class FilterLabel(TexMobject):
|
|
|
|
def __init__(self, tex, degrees, **kwargs):
|
|
|
|
TexMobject.__init__(self, tex + " \\uparrow", **kwargs)
|
|
|
|
self[-1].rotate(-degrees * np.pi / 180)
|
|
|
|
|
|
|
|
class PolarizingFilter(Circle):
|
|
|
|
CONFIG = {
|
|
|
|
"stroke_color" : DARK_GREY,
|
|
|
|
"fill_color" : LIGHT_GREY,
|
|
|
|
"fill_opacity" : 0.5,
|
|
|
|
"label_tex" : None,
|
|
|
|
"filter_angle" : 0,
|
2017-08-25 17:56:55 -07:00
|
|
|
"include_arrow_label" : True,
|
2017-08-24 23:12:25 -07:00
|
|
|
}
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
Circle.__init__(self, **kwargs)
|
|
|
|
|
|
|
|
if self.label_tex:
|
|
|
|
self.label = TexMobject(self.label_tex)
|
|
|
|
self.label.next_to(self.get_top(), DOWN, SMALL_BUFF)
|
|
|
|
self.add(self.label)
|
|
|
|
|
2017-08-25 17:56:55 -07:00
|
|
|
arrow = Arrow(
|
|
|
|
ORIGIN, 0.7*UP,
|
|
|
|
color = WHITE,
|
|
|
|
buff = 0,
|
|
|
|
)
|
2017-08-24 23:12:25 -07:00
|
|
|
arrow.shift(self.get_top())
|
|
|
|
arrow.rotate(-self.filter_angle)
|
|
|
|
self.add(arrow)
|
|
|
|
self.arrow = arrow
|
2017-08-25 17:56:55 -07:00
|
|
|
shade_in_3d(self)
|
2017-08-24 23:12:25 -07:00
|
|
|
|
2017-08-25 17:56:55 -07:00
|
|
|
if self.include_arrow_label:
|
|
|
|
arrow_label = TexMobject(
|
|
|
|
"%.1f^\\circ"%(self.filter_angle*180/np.pi)
|
|
|
|
)
|
|
|
|
arrow_label.add_background_rectangle()
|
|
|
|
arrow_label.next_to(arrow.get_tip(), UP)
|
|
|
|
self.add(arrow_label)
|
|
|
|
self.arrow_label = arrow_label
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
################
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
class IntroduceElectricField(PiCreatureScene):
|
|
|
|
CONFIG = {
|
|
|
|
"vector_field_colors" : [BLUE_B, BLUE_D],
|
|
|
|
"max_vector_length" : 0.9,
|
|
|
|
}
|
2017-08-24 11:44:06 -07:00
|
|
|
def construct(self):
|
2017-08-30 13:16:08 -07:00
|
|
|
self.write_title()
|
|
|
|
self.draw_field()
|
|
|
|
self.add_particle()
|
|
|
|
self.let_particle_wander()
|
2017-08-24 23:12:25 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
def write_title(self):
|
|
|
|
morty = self.pi_creature
|
|
|
|
|
|
|
|
title = TextMobject(
|
|
|
|
"Electro", "magnetic", " field",
|
|
|
|
arg_separator = ""
|
|
|
|
)
|
|
|
|
title.next_to(morty, UP+LEFT)
|
|
|
|
electric = TextMobject("Electric")
|
|
|
|
electric.next_to(title[-1], LEFT)
|
|
|
|
electric.highlight(BLUE)
|
|
|
|
|
|
|
|
title.save_state()
|
|
|
|
title.shift(DOWN)
|
|
|
|
title.fade(1)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
title.restore,
|
|
|
|
morty.change, "raise_right_hand",
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
title[0].highlight, BLUE,
|
|
|
|
title[1].highlight, YELLOW,
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
self.play(
|
|
|
|
ShrinkToCenter(title[1]),
|
|
|
|
Transform(title[0], electric)
|
|
|
|
)
|
|
|
|
|
|
|
|
title.add_background_rectangle()
|
|
|
|
self.title = title
|
|
|
|
|
|
|
|
def draw_field(self):
|
|
|
|
morty = self.pi_creature
|
|
|
|
vector_field = self.get_vector_field()
|
|
|
|
self.play(
|
|
|
|
LaggedStart(
|
|
|
|
ShowCreation, vector_field,
|
|
|
|
run_time = 3
|
|
|
|
),
|
|
|
|
self.title.center,
|
|
|
|
self.title.scale, 1.5,
|
|
|
|
self.title.to_edge, UP,
|
|
|
|
morty.change, "happy", ORIGIN,
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
self.vector_field = vector_field
|
|
|
|
|
|
|
|
def add_particle(self):
|
|
|
|
morty = self.pi_creature
|
|
|
|
point = UP+LEFT + SMALL_BUFF*(UP+RIGHT)
|
|
|
|
particle = self.get_particle()
|
|
|
|
particle.move_to(point)
|
|
|
|
|
|
|
|
vector = self.get_vector(particle.get_center())
|
|
|
|
vector.highlight(RED)
|
|
|
|
vector.scale(1.5, about_point = point)
|
|
|
|
vector.shift(SMALL_BUFF*vector.get_vector())
|
|
|
|
force = TextMobject("Force")
|
|
|
|
force.next_to(ORIGIN, UP+RIGHT, SMALL_BUFF)
|
|
|
|
force.rotate(vector.get_angle())
|
|
|
|
force.shift(vector.get_start())
|
|
|
|
|
|
|
|
particle.save_state()
|
|
|
|
particle.move_to(morty.get_left() + 0.5*UP + 0.2*RIGHT)
|
|
|
|
particle.fade(1)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
particle.restore,
|
|
|
|
morty.change, "raise_right_hand",
|
|
|
|
)
|
|
|
|
self.play(morty.change, "thinking", particle)
|
|
|
|
self.play(
|
|
|
|
ShowCreation(vector),
|
|
|
|
Write(force, run_time = 1),
|
|
|
|
)
|
2017-08-27 23:52:28 -07:00
|
|
|
self.dither(2)
|
2017-08-24 23:12:25 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
self.particle = particle
|
|
|
|
self.force_vector = VGroup(vector, force)
|
|
|
|
|
|
|
|
def let_particle_wander(self):
|
|
|
|
possible_points = [v.get_start() for v in self.vector_field]
|
|
|
|
points = random.sample(possible_points, 45)
|
|
|
|
points.append(3*UP+3*LEFT)
|
|
|
|
particles = VGroup(self.particle, *[
|
|
|
|
self.particle.copy().move_to(point)
|
|
|
|
for point in points
|
|
|
|
])
|
|
|
|
for particle in particles:
|
|
|
|
particle.velocity = np.zeros(3)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(self.force_vector),
|
|
|
|
LaggedStart(FadeIn, VGroup(*particles[1:]))
|
|
|
|
)
|
|
|
|
self.moving_particles = particles
|
|
|
|
self.add_foreground_mobjects(self.moving_particles, self.pi_creature)
|
|
|
|
self.always_continually_update = True
|
|
|
|
self.dither(10)
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
def continual_update(self):
|
|
|
|
Scene.continual_update(self)
|
|
|
|
if hasattr(self, "moving_particles"):
|
|
|
|
dt = self.frame_duration
|
|
|
|
for p in self.moving_particles:
|
|
|
|
vect = self.field_function(p.get_center())
|
|
|
|
p.velocity += vect*dt
|
|
|
|
p.shift(p.velocity*dt)
|
|
|
|
self.pi_creature.look_at(self.moving_particles[-1])
|
|
|
|
|
|
|
|
def get_particle(self):
|
|
|
|
particle = Circle(radius = 0.2)
|
|
|
|
particle.set_stroke(RED, 3)
|
|
|
|
particle.set_fill(RED, 0.5)
|
|
|
|
plus = TexMobject("+")
|
|
|
|
plus.scale(0.7)
|
|
|
|
plus.move_to(particle)
|
|
|
|
particle.add(plus)
|
|
|
|
return particle
|
|
|
|
|
|
|
|
def get_vector_field(self):
|
|
|
|
result = VGroup(*[
|
|
|
|
self.get_vector(point)
|
|
|
|
for x in np.arange(-9, 9)
|
|
|
|
for y in np.arange(-5, 5)
|
|
|
|
for point in [x*RIGHT + y*UP]
|
|
|
|
])
|
|
|
|
shading_list = list(result)
|
|
|
|
shading_list.sort(
|
|
|
|
lambda m1, m2 : cmp(m1.get_length(), m2.get_length())
|
|
|
|
)
|
|
|
|
VGroup(*shading_list).gradient_highlight(*self.vector_field_colors)
|
|
|
|
result.set_fill(opacity = 0.75)
|
|
|
|
result.sort_submobjects(np.linalg.norm)
|
|
|
|
|
|
|
|
return result
|
2017-08-24 23:12:25 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
def get_vector(self, point):
|
|
|
|
return Vector(self.field_function(point)).shift(point)
|
2017-08-27 23:52:28 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
def field_function(self, point):
|
|
|
|
x, y = point[:2]
|
|
|
|
result = y*RIGHT + np.sin(x)*UP
|
|
|
|
return self.normalized(result)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
def normalized(self, vector):
|
|
|
|
norm = np.linalg.norm(vector) or 1
|
|
|
|
target_length = self.max_vector_length * sigmoid(0.1*norm)
|
|
|
|
return target_length * vector/norm
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
class IntroduceMagneticField(IntroduceElectricField, ThreeDScene):
|
|
|
|
CONFIG = {
|
|
|
|
"vector_field_colors" : [YELLOW_C, YELLOW_D]
|
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
IntroduceElectricField.setup(self)
|
|
|
|
self.remove(self.pi_creature)
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
self.set_camera_position(0.1, -np.pi/2)
|
|
|
|
self.add_title()
|
|
|
|
self.add_vector_field()
|
|
|
|
self.introduce_moving_charge()
|
|
|
|
self.show_force()
|
|
|
|
# self.many_charges()
|
|
|
|
|
|
|
|
def add_title(self):
|
|
|
|
title = TextMobject("Magnetic", "field")
|
|
|
|
title[0].highlight(YELLOW)
|
|
|
|
title.scale(1.5)
|
|
|
|
title.to_edge(UP)
|
|
|
|
title.add_background_rectangle()
|
|
|
|
|
|
|
|
self.add(title)
|
|
|
|
self.title = title
|
|
|
|
|
|
|
|
def add_vector_field(self):
|
|
|
|
vector_field = self.get_vector_field()
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
LaggedStart(ShowCreation, vector_field, run_time = 3),
|
|
|
|
Animation(self.title)
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
def introduce_moving_charge(self):
|
|
|
|
point = 3*RIGHT + UP
|
|
|
|
particle = self.get_particle()
|
|
|
|
particle.move_to(point)
|
|
|
|
|
|
|
|
velocity = Vector(2*RIGHT).shift(particle.get_right())
|
|
|
|
velocity.highlight(WHITE)
|
|
|
|
velocity_word = TextMobject("Velocity")
|
|
|
|
velocity_word.highlight(velocity.get_color())
|
|
|
|
velocity_word.add_background_rectangle()
|
|
|
|
velocity_word.next_to(velocity, UP, 0, LEFT)
|
|
|
|
|
|
|
|
M_vect = self.get_vector(point)
|
|
|
|
M_vect.highlight(YELLOW)
|
|
|
|
M_vect.shift(SMALL_BUFF*M_vect.get_vector())
|
|
|
|
|
|
|
|
particle.save_state()
|
|
|
|
particle.shift(2*SPACE_WIDTH*LEFT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
particle.restore,
|
|
|
|
run_time = 2,
|
|
|
|
rate_func = None,
|
|
|
|
)
|
|
|
|
self.add(velocity)
|
|
|
|
self.play(Write(velocity_word, run_time = 0.5))
|
|
|
|
# self.play(ShowCreation(M_vect))
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
self.particle = particle
|
|
|
|
|
|
|
|
def show_force(self):
|
|
|
|
point = self.particle.get_center()
|
|
|
|
F_vect = Vector(
|
|
|
|
3*np.cross(self.field_function(point), RIGHT),
|
|
|
|
color = GREEN
|
|
|
|
)
|
|
|
|
F_vect.shift(point)
|
|
|
|
F_word = TextMobject("Force")
|
|
|
|
F_word.rotate(np.pi/2, RIGHT)
|
|
|
|
F_word.next_to(F_vect, OUT)
|
|
|
|
F_word.highlight(F_vect.get_color())
|
|
|
|
F_eq = TexMobject(
|
|
|
|
"=","q", "\\textbf{v}", "\\times", "\\textbf{B}"
|
|
|
|
)
|
|
|
|
F_eq.highlight_by_tex_to_color_map({
|
|
|
|
"q" : RED,
|
|
|
|
"B" : YELLOW,
|
|
|
|
})
|
|
|
|
F_eq.rotate(np.pi/2, RIGHT)
|
|
|
|
F_eq.next_to(F_word, RIGHT)
|
|
|
|
|
|
|
|
|
|
|
|
self.move_camera(0.8*np.pi/2, -0.55*np.pi)
|
|
|
|
self.begin_ambient_camera_rotation()
|
|
|
|
self.play(ShowCreation(F_vect))
|
|
|
|
self.play(Write(F_word))
|
|
|
|
self.dither()
|
|
|
|
self.play(Write(F_eq))
|
|
|
|
self.dither(8)
|
|
|
|
|
|
|
|
def many_charges(self):
|
|
|
|
charges = VGroup()
|
|
|
|
for y in range(2, 3):
|
|
|
|
charge = self.get_particle()
|
|
|
|
charge.move_to(3*LEFT + y*UP)
|
|
|
|
charge.velocity = (2*RIGHT).astype('float')
|
|
|
|
charges.add(charge)
|
|
|
|
|
|
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.add_foreground_mobjects(*charges)
|
|
|
|
self.moving_particles = charges
|
|
|
|
self.dither(5)
|
|
|
|
|
|
|
|
|
|
|
|
###
|
|
|
|
|
|
|
|
def continual_update(self):
|
|
|
|
Scene.continual_update(self)
|
|
|
|
if hasattr(self, "moving_particles"):
|
|
|
|
dt = self.frame_duration
|
|
|
|
for p in self.moving_particles:
|
|
|
|
M_vect = self.field_function(p.get_center())
|
|
|
|
F_vect = 3*np.cross(p.velocity, M_vect)
|
|
|
|
p.velocity += F_vect*dt
|
|
|
|
p.shift(p.velocity*dt)
|
|
|
|
|
|
|
|
def field_function(self, point):
|
|
|
|
x, y = point[:2]
|
|
|
|
y += 0.5
|
|
|
|
gauss = lambda r : np.exp(-0.5*r**2)
|
|
|
|
result = (y**2 - 1)*RIGHT + x*(gauss(y+2) - gauss(y-2))*UP
|
|
|
|
return self.normalized(result)
|
|
|
|
|
|
|
|
class CurlRelationBetweenFields(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
self.add_axes()
|
|
|
|
self.loop_in_E()
|
|
|
|
self.loop_in_M()
|
|
|
|
self.second_loop_in_E()
|
|
|
|
|
|
|
|
def add_axes(self):
|
|
|
|
self.add(ThreeDAxes(x_axis_radius = SPACE_WIDTH))
|
|
|
|
|
|
|
|
def loop_in_E(self):
|
|
|
|
E_vects = VGroup(*[
|
|
|
|
Vector(0.5*rotate_vector(vect, np.pi/2)).shift(vect)
|
|
|
|
for vect in compass_directions(8)
|
|
|
|
])
|
|
|
|
E_vects.highlight(E_COLOR)
|
|
|
|
point = 1.2*RIGHT + 2*UP + OUT
|
|
|
|
E_vects.shift(point)
|
|
|
|
|
|
|
|
M_vect = Vector(
|
|
|
|
IN,
|
|
|
|
normal_vector = DOWN,
|
|
|
|
color = M_COLOR
|
|
|
|
)
|
|
|
|
M_vect.shift(point)
|
|
|
|
M_vect.save_state()
|
|
|
|
M_vect.scale(0.01, about_point = M_vect.get_start())
|
|
|
|
|
|
|
|
self.play(ShowCreation(E_vects, run_time = 2))
|
|
|
|
self.dither()
|
|
|
|
self.move_camera(0.8*np.pi/2, -0.45*np.pi)
|
|
|
|
self.begin_ambient_camera_rotation()
|
|
|
|
self.play(M_vect.restore, run_time = 3, rate_func = None)
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
self.E_vects = E_vects
|
|
|
|
self.E_circle_center = point
|
|
|
|
self.M_vect = M_vect
|
|
|
|
|
|
|
|
def loop_in_M(self):
|
|
|
|
M_vects = VGroup(*[
|
|
|
|
Vector(
|
|
|
|
rotate_vector(vect, np.pi/2),
|
|
|
|
normal_vector = IN,
|
|
|
|
color = M_COLOR
|
|
|
|
).shift(vect)
|
|
|
|
for vect in compass_directions(8, LEFT)[1:]
|
|
|
|
])
|
|
|
|
M_vects.rotate(np.pi/2, RIGHT)
|
|
|
|
new_point = self.E_circle_center + RIGHT
|
|
|
|
M_vects.shift(new_point)
|
|
|
|
|
|
|
|
E_vect = self.E_vects[0]
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(M_vects, run_time = 2),
|
|
|
|
*map(FadeOut, self.E_vects[1:])
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
self.play(
|
|
|
|
E_vect.rotate, np.pi, RIGHT, [], new_point,
|
|
|
|
E_vect.scale_about_point, 3, new_point,
|
|
|
|
run_time = 4,
|
|
|
|
rate_func = None,
|
|
|
|
)
|
|
|
|
self.dither()
|
|
|
|
|
|
|
|
self.M_circle_center = new_point
|
|
|
|
M_vects.add(self.M_vect)
|
|
|
|
self.M_vects = M_vects
|
|
|
|
self.E_vect = E_vect
|
|
|
|
|
|
|
|
def second_loop_in_E(self):
|
|
|
|
E_vects = VGroup(*[
|
|
|
|
Vector(1.5*rotate_vector(vect, np.pi/2)).shift(vect)
|
|
|
|
for vect in compass_directions(8, LEFT)[1:]
|
|
|
|
])
|
|
|
|
E_vects.highlight(E_COLOR)
|
|
|
|
point = self.M_circle_center + RIGHT
|
|
|
|
E_vects.shift(point)
|
|
|
|
|
|
|
|
M_vect = self.M_vects[3]
|
|
|
|
self.M_vects.remove(M_vect)
|
|
|
|
|
|
|
|
self.play(FadeOut(self.M_vects))
|
|
|
|
self.play(ShowCreation(E_vects), Animation(M_vect))
|
|
|
|
self.play(
|
|
|
|
M_vect.rotate, np.pi, RIGHT, [], point,
|
|
|
|
run_time = 5,
|
|
|
|
rate_func = None,
|
|
|
|
)
|
|
|
|
self.dither(3)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
class WriteCurlEquations(Scene):
|
|
|
|
def construct(self):
|
|
|
|
eq1 = TexMobject(
|
|
|
|
"\\nabla \\times", "\\textbf{E}", "=",
|
|
|
|
"-\\frac{1}{c}",
|
|
|
|
"\\frac{\\partial \\textbf{B}}{\\partial t}"
|
|
|
|
)
|
|
|
|
eq2 = TexMobject(
|
|
|
|
"\\nabla \\times", "\\textbf{B}", "=^*",
|
|
|
|
"\\frac{1}{c}",
|
|
|
|
"\\frac{\\partial \\textbf{E}}{\\partial t}"
|
|
|
|
)
|
|
|
|
footnote = TextMobject("*Ignoring currents")
|
|
|
|
footnote.scale(0.7)
|
|
|
|
eqs = VGroup(eq1, eq2, footnote)
|
|
|
|
eqs.arrange_submobjects(DOWN, buff = LARGE_BUFF)
|
|
|
|
eqs.scale_to_fit_height(2*SPACE_HEIGHT - 1)
|
|
|
|
for eq in eqs:
|
|
|
|
eq.highlight_by_tex_to_color_map({
|
|
|
|
"E" : E_COLOR,
|
|
|
|
"B" : M_COLOR,
|
|
|
|
})
|
|
|
|
|
|
|
|
self.play(Write(eq1, run_time = 2))
|
|
|
|
self.dither(3)
|
|
|
|
self.play(Write(eq2, run_time = 2))
|
|
|
|
self.play(FadeIn(footnote))
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
class IntroduceEMWave(ThreeDScene):
|
|
|
|
CONFIG = {
|
|
|
|
"EMWave_config" : {
|
|
|
|
"requires_start_up" : True
|
|
|
|
}
|
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
self.axes = ThreeDAxes()
|
|
|
|
self.add(self.axes)
|
|
|
|
self.em_wave = EMWave(**self.EMWave_config)
|
|
|
|
self.add(self.em_wave)
|
|
|
|
self.set_camera_position(0.8*np.pi/2, -0.7*np.pi)
|
|
|
|
self.begin_ambient_camera_rotation()
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
def construct(self):
|
|
|
|
words = TextMobject(
|
|
|
|
"Electro", "magnetic", " radiation",
|
|
|
|
arg_separator = ""
|
|
|
|
)
|
|
|
|
words.highlight_by_tex_to_color_map({
|
|
|
|
"Electro" : E_COLOR,
|
|
|
|
"magnetic" : M_COLOR,
|
|
|
|
})
|
|
|
|
words.next_to(ORIGIN, LEFT, MED_LARGE_BUFF)
|
|
|
|
words.to_edge(UP)
|
|
|
|
words.rotate(np.pi/2, RIGHT)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
self.dither(7)
|
|
|
|
self.play(Write(words, run_time = 2))
|
|
|
|
self.dither(20)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
#####
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
class SimpleEMWave(IntroduceEMWave):
|
|
|
|
def construct(self):
|
|
|
|
self.dither(30)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 13:16:08 -07:00
|
|
|
class ListRelevantWaveIdeas(TeacherStudentsScene):
|
|
|
|
def construct(self):
|
|
|
|
title = TextMobject("Wave","topics")
|
|
|
|
title.to_corner(UP + LEFT, LARGE_BUFF)
|
|
|
|
title.highlight(BLUE)
|
|
|
|
h_line = Line(title.get_left(), title.get_right())
|
|
|
|
h_line.next_to(title, DOWN, SMALL_BUFF)
|
|
|
|
|
|
|
|
topics = VGroup(*map(TextMobject, [
|
|
|
|
"- Superposition",
|
|
|
|
"- Amplitudes",
|
|
|
|
"- Phase influences addition",
|
|
|
|
]))
|
|
|
|
topics.scale(0.8)
|
|
|
|
topics.arrange_submobjects(DOWN, aligned_edge = LEFT)
|
|
|
|
topics.next_to(h_line, DOWN, aligned_edge = LEFT)
|
|
|
|
|
|
|
|
quantum = TextMobject("Quantum")
|
|
|
|
quantum.highlight(GREEN)
|
|
|
|
quantum.move_to(title[0], LEFT)
|
|
|
|
|
|
|
|
wave_point = self.teacher.get_corner(UP+LEFT) + 2*UP
|
|
|
|
self.play(
|
|
|
|
Animation(VectorizedPoint(wave_point)),
|
|
|
|
self.teacher.change, "raise_right_hand"
|
|
|
|
)
|
|
|
|
self.dither(2)
|
|
|
|
self.play(
|
|
|
|
Write(title, run_time = 2),
|
|
|
|
ShowCreation(h_line)
|
|
|
|
)
|
|
|
|
self.change_student_modes(
|
|
|
|
*["pondering"]*3,
|
|
|
|
added_anims = [LaggedStart(
|
|
|
|
FadeIn, topics,
|
|
|
|
run_time = 3
|
|
|
|
)],
|
|
|
|
look_at_arg = title
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Animation(title),
|
|
|
|
self.teacher.change, "happy"
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
title[0].next_to, quantum.copy(), UP, MED_SMALL_BUFF, LEFT,
|
|
|
|
title[0].fade, 0.5,
|
|
|
|
title[1].next_to, quantum.copy(), RIGHT, 2*SMALL_BUFF,
|
|
|
|
Write(quantum),
|
|
|
|
)
|
|
|
|
self.dither(5)
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 18:33:55 -07:00
|
|
|
class DirectWaveOutOfScreen(IntroduceEMWave):
|
|
|
|
CONFIG = {
|
|
|
|
"EMWave_config" : {
|
|
|
|
"requires_start_up" : False,
|
|
|
|
"amplitude" : 2,
|
|
|
|
"start_point" : SPACE_WIDTH*LEFT,
|
|
|
|
"A_vect" : [0, 1, 0],
|
|
|
|
"start_up_time" : 0,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
def setup(self):
|
|
|
|
IntroduceEMWave.setup(self)
|
|
|
|
self.remove(self.axes)
|
|
|
|
for ov in self.em_wave.continual_animations:
|
|
|
|
ov.vector.normal_vector = RIGHT
|
|
|
|
self.set_camera_position(0.9*np.pi/2, -0.3*np.pi)
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
self.move_into_position()
|
|
|
|
|
|
|
|
def move_into_position(self):
|
|
|
|
self.dither(2)
|
|
|
|
self.continual_update()
|
|
|
|
faded_vectors = VGroup(*[
|
|
|
|
ov.vector
|
|
|
|
for ov in self.em_wave.continual_animations[:-2]
|
|
|
|
])
|
|
|
|
self.move_camera(
|
|
|
|
0.99*np.pi/2, -0.01,
|
|
|
|
run_time = 2,
|
|
|
|
added_anims = [faded_vectors.set_fill, None, 0.5]
|
|
|
|
)
|
|
|
|
self.stop_ambient_camera_rotation()
|
|
|
|
self.move_camera(
|
|
|
|
np.pi/2, 0,
|
|
|
|
added_anims = [faded_vectors.set_fill, None, 0.05],
|
|
|
|
run_time = 2,
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
self.em_wave.M_vects.set_fill, None, 0
|
|
|
|
)
|
|
|
|
self.dither(2)
|
|
|
|
self.play(faded_vectors.set_fill, None, 0)
|
|
|
|
self.dither(4)
|
|
|
|
|
|
|
|
class ShowVectorEquation(Scene):
|
|
|
|
def construct(self):
|
|
|
|
self.force_skipping()
|
|
|
|
|
|
|
|
self.add_vector()
|
|
|
|
self.add_plane()
|
|
|
|
self.write_horizontally_polarized()
|
|
|
|
self.write_components()
|
|
|
|
self.show_graph()
|
|
|
|
self.add_phi()
|
|
|
|
self.add_amplitude()
|
|
|
|
self.add_kets()
|
|
|
|
self.switch_to_vertically_polarized_light()
|
|
|
|
|
|
|
|
def add_vector(self):
|
|
|
|
self.vector = Vector(2*RIGHT, color = E_COLOR)
|
|
|
|
self.oscillating_vector = OscillatingVector(
|
|
|
|
self.vector,
|
|
|
|
A_vect = [2, 0, 0],
|
|
|
|
frequency = 0.25,
|
|
|
|
)
|
|
|
|
self.add(self.oscillating_vector)
|
|
|
|
self.revert_to_original_skipping_status()
|
|
|
|
self.dither(3)
|
|
|
|
|
|
|
|
def add_plane(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def write_horizontally_polarized(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def write_components(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def show_graph(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def add_phi(self):
|
|
|
|
pass
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 18:33:55 -07:00
|
|
|
def add_amplitude(self):
|
|
|
|
pass
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 18:33:55 -07:00
|
|
|
def add_kets(self):
|
|
|
|
pass
|
2017-08-24 11:44:06 -07:00
|
|
|
|
2017-08-30 18:33:55 -07:00
|
|
|
def switch_to_vertically_polarized_light(self):
|
|
|
|
pass
|
2017-08-24 11:44:06 -07:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|