mirror of
https://github.com/3b1b/manim.git
synced 2025-11-14 12:27:44 +00:00
Beginning waves video animations
This commit is contained in:
parent
6539160789
commit
ea8381de71
10 changed files with 587 additions and 90 deletions
|
|
@ -75,6 +75,17 @@ class AmbientRotation(ContinualAnimation):
|
|||
|
||||
|
||||
|
||||
class AmbientMovement(ContinualAnimation):
|
||||
CONFIG = {
|
||||
"direction" : RIGHT,
|
||||
"rate" : 0.05, #Units per second
|
||||
}
|
||||
|
||||
def update_mobject(self, dt):
|
||||
self.mobject.shift(dt*self.rate*self.direction)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ class UpdateFromFunc(Animation):
|
|||
class UpdateFromAlphaFunc(UpdateFromFunc):
|
||||
def update_mobject(self, alpha):
|
||||
self.update_function(self.mobject, alpha)
|
||||
|
||||
|
||||
class MaintainPositionRelativeTo(Animation):
|
||||
CONFIG = {
|
||||
"tracked_critical_point" : ORIGIN
|
||||
|
|
|
|||
|
|
@ -114,9 +114,7 @@ class SpinInFromNothing(GrowFromCenter):
|
|||
class ShrinkToCenter(Transform):
|
||||
def __init__(self, mobject, **kwargs):
|
||||
Transform.__init__(
|
||||
self, mobject,
|
||||
Point(mobject.get_center()),
|
||||
**kwargs
|
||||
self, mobject, mobject.get_point_mobject(), **kwargs
|
||||
)
|
||||
|
||||
class ApplyMethod(Transform):
|
||||
|
|
@ -141,7 +139,6 @@ class ApplyMethod(Transform):
|
|||
method.im_func(target, *args, **method_kwargs)
|
||||
Transform.__init__(self, method.im_self, target, **kwargs)
|
||||
|
||||
|
||||
class FadeOut(Transform):
|
||||
CONFIG = {
|
||||
"remover" : True,
|
||||
|
|
@ -167,7 +164,6 @@ class FadeIn(Transform):
|
|||
self.starting_mobject.set_stroke(width = 0)
|
||||
self.starting_mobject.set_fill(opacity = 0)
|
||||
|
||||
|
||||
class ShimmerIn(DelayByOrder):
|
||||
def __init__(self, mobject, **kwargs):
|
||||
mobject.sort_points(lambda p : np.dot(p, DOWN+RIGHT))
|
||||
|
|
|
|||
36
bell.py
36
bell.py
|
|
@ -828,7 +828,7 @@ class ForgetPreviousActions(PhotonsThroughPerpendicularFilters):
|
|||
prob_words.next_to(rect1, OUT)
|
||||
|
||||
self.add(rect1)
|
||||
self.play(Write(prob_words))
|
||||
self.play(FadeIn(prob_words))
|
||||
# for x in range(2):
|
||||
# self.shoot_photon()
|
||||
|
||||
|
|
@ -845,10 +845,9 @@ class ForgetPreviousActions(PhotonsThroughPerpendicularFilters):
|
|||
ShowCreation(rect2),
|
||||
Write(ignore_words, run_time = 1)
|
||||
)
|
||||
self.shoot_photon()
|
||||
self.dither(2)
|
||||
# for x in range(4):
|
||||
# self.shoot_photon()
|
||||
for x in range(3):
|
||||
self.shoot_photon()
|
||||
self.dither()
|
||||
|
||||
|
||||
def shoot_photon(self):
|
||||
|
|
@ -1117,9 +1116,12 @@ class VennDiagramProofByContradiction(Scene):
|
|||
])
|
||||
venn_diagram.center()
|
||||
props = [1./12, 0.5, 0]
|
||||
for circle, char, prop in zip(venn_diagram, "ABC", props):
|
||||
label = TextMobject("Would pass \\\\ through", char)
|
||||
angles = [0, np.pi/8, np.pi/4]
|
||||
for circle, char, prop, angle in zip(venn_diagram, "ABC", props, angles):
|
||||
label = TextMobject("Would pass \\\\ through", char + "$\\! \\uparrow$")
|
||||
label.highlight_by_tex(char, circle.get_color())
|
||||
label[1][1].rotate_in_place(-angle)
|
||||
label[1][1].shift(0.5*SMALL_BUFF*UP)
|
||||
center = circle.get_center()
|
||||
label.move_to(center)
|
||||
label.generate_target()
|
||||
|
|
@ -1146,7 +1148,6 @@ class VennDiagramProofByContradiction(Scene):
|
|||
self.venn_diagram = venn_diagram
|
||||
for part in self.venn_diagram:
|
||||
part.save_state()
|
||||
|
||||
if send_to_corner:
|
||||
self.play(
|
||||
self.venn_diagram.scale, 0.25,
|
||||
|
|
@ -1839,7 +1840,7 @@ class NoFirstMeasurementPreferenceBasedOnDirection(ShowVariousFilterPairs):
|
|||
added_anims = list(it.chain(*[
|
||||
[
|
||||
pf.arrow_label.rotate, np.pi/2, OUT,
|
||||
pf.arrow_label.next_to, pf.arrow, OUT+RIGHT
|
||||
pf.arrow_label.next_to, pf.arrow, OUT+RIGHT, SMALL_BUFF
|
||||
]
|
||||
for pf in self.pol_filters
|
||||
] + [[FadeIn(words)]]))
|
||||
|
|
@ -1857,12 +1858,17 @@ class NoFirstMeasurementPreferenceBasedOnDirection(ShowVariousFilterPairs):
|
|||
all_pre_lines.add(*pre_lines)
|
||||
all_post_lines.add(*post_lines)
|
||||
|
||||
for lines in all_pre_lines, all_post_lines:
|
||||
self.play(ShowCreation(
|
||||
lines,
|
||||
rate_func = None,
|
||||
submobject_mode = "all_at_once"
|
||||
))
|
||||
kwargs = {
|
||||
"rate_func" : None,
|
||||
"submobject_mode" : "all_at_once"
|
||||
}
|
||||
self.play(ShowCreation(all_pre_lines, **kwargs))
|
||||
self.play(
|
||||
ShowCreation(all_post_lines, **kwargs),
|
||||
Animation(self.pol_filters),
|
||||
Animation(all_pre_lines),
|
||||
)
|
||||
self.add_foreground_mobject(all_pre_lines)
|
||||
self.dither(7)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ class Scene(object):
|
|||
animations.pop()
|
||||
#method should already have target then.
|
||||
else:
|
||||
mobject.target = mobject.copy()
|
||||
mobject.target = mobject.deepcopy()
|
||||
state["curr_method"].im_func(
|
||||
mobject.target, *state["method_args"]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -109,7 +109,10 @@ class PiCreature(SVGMobject):
|
|||
return self
|
||||
|
||||
def look(self, direction):
|
||||
direction = direction/np.linalg.norm(direction)
|
||||
norm = np.linalg.norm(direction)
|
||||
if norm == 0:
|
||||
return
|
||||
direction /= norm
|
||||
self.purposeful_looking_direction = direction
|
||||
for pupil, eye in zip(self.pupils.split(), self.eyes.split()):
|
||||
pupil_radius = pupil.get_width()/2.
|
||||
|
|
|
|||
|
|
@ -120,6 +120,9 @@ class Line(VMobject):
|
|||
def get_start_and_end(self):
|
||||
return self.get_start(), self.get_end()
|
||||
|
||||
def get_vector(self):
|
||||
return self.get_end() - self.get_start()
|
||||
|
||||
def get_start(self):
|
||||
return np.array(self.points[0])
|
||||
|
||||
|
|
|
|||
|
|
@ -166,12 +166,13 @@ class Axes(VGroup):
|
|||
**self.number_line_config
|
||||
)
|
||||
self.z_axis.rotate(-np.pi/2, UP)
|
||||
self.z_axis.rotate(np.pi/2, OUT)
|
||||
self.add(self.z_axis)
|
||||
|
||||
class ThreeDAxes(Axes):
|
||||
CONFIG = {
|
||||
"x_axis_radius" : 5,
|
||||
"y_axis_radius" : 5,
|
||||
"x_axis_radius" : 5.5,
|
||||
"y_axis_radius" : 4.5,
|
||||
"three_d" : True,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
|||
from topics.geometry import Square, Line
|
||||
from scene import Scene
|
||||
from camera import Camera
|
||||
from animation.continual_animation import AmbientRotation
|
||||
from animation.continual_animation import AmbientMovement
|
||||
from animation.transform import ApplyMethod
|
||||
|
||||
class CameraWithPerspective(Camera):
|
||||
|
|
@ -35,7 +35,8 @@ class ThreeDCamera(CameraWithPerspective):
|
|||
def __init__(self, *args, **kwargs):
|
||||
Camera.__init__(self, *args, **kwargs)
|
||||
self.unit_sun_vect = self.sun_vect/np.linalg.norm(self.sun_vect)
|
||||
self.position_mobject = VectorizedPoint()
|
||||
## Lives in the phi-theta-distance space
|
||||
self.rotation_mobject = VectorizedPoint()
|
||||
self.set_position(self.phi, self.theta, self.distance)
|
||||
|
||||
def get_color(self, method):
|
||||
|
|
@ -94,24 +95,23 @@ class ThreeDCamera(CameraWithPerspective):
|
|||
self, sorted(vmobjects, cmp = z_cmp)
|
||||
)
|
||||
|
||||
def get_position(self):
|
||||
return self.position_mobject.points[0]
|
||||
def get_spherical_coords(self, phi = None, theta = None, distance = None):
|
||||
curr_phi, curr_theta, curr_d = self.rotation_mobject.points[0]
|
||||
phi = phi or curr_phi
|
||||
theta = theta or curr_theta
|
||||
distance = distance or curr_d
|
||||
return np.array([phi, theta, distance])
|
||||
|
||||
def get_phi(self):
|
||||
x, y, z = self.get_position()
|
||||
return angle_of_vector([z, np.sqrt(x**2 + y**2)])
|
||||
return self.get_spherical_coords()[0]
|
||||
|
||||
def get_theta(self):
|
||||
x, y, z = self.get_position()
|
||||
return angle_of_vector([x, y])
|
||||
return self.get_spherical_coords()[1]
|
||||
|
||||
def get_distance(self):
|
||||
return np.linalg.norm(self.get_position())
|
||||
return self.get_spherical_coords()[2]
|
||||
|
||||
def spherical_coords_to_point(self, phi, theta, distance):
|
||||
phi = phi or self.get_phi()
|
||||
theta = theta or self.get_theta()
|
||||
distance = distance or self.get_distance()
|
||||
return distance*np.array([
|
||||
np.sin(phi)*np.cos(theta),
|
||||
np.sin(phi)*np.sin(theta),
|
||||
|
|
@ -119,11 +119,9 @@ class ThreeDCamera(CameraWithPerspective):
|
|||
])
|
||||
|
||||
def set_position(self, phi = None, theta = None, distance = None):
|
||||
point = self.spherical_coords_to_point(phi, theta, distance)
|
||||
self.position_mobject.move_to(point)
|
||||
self.phi = self.get_phi()
|
||||
self.theta = self.get_theta()
|
||||
self.distance = self.get_distance()
|
||||
point = self.get_spherical_coords(phi, theta, distance)
|
||||
self.rotation_mobject.move_to(point)
|
||||
self.phi, self.theta, self.distance = point
|
||||
|
||||
def get_view_transformation_matrix(self):
|
||||
return np.dot(
|
||||
|
|
@ -145,9 +143,9 @@ class ThreeDScene(Scene):
|
|||
self.camera.set_position(phi, theta, distance)
|
||||
|
||||
def begin_ambient_camera_rotation(self, rate = 0.01):
|
||||
self.ambient_camera_rotation = AmbientRotation(
|
||||
self.camera.position_mobject,
|
||||
axis = OUT,
|
||||
self.ambient_camera_rotation = AmbientMovement(
|
||||
self.camera.rotation_mobject,
|
||||
direction = UP,
|
||||
rate = rate
|
||||
)
|
||||
self.add(self.ambient_camera_rotation)
|
||||
|
|
@ -161,9 +159,9 @@ class ThreeDScene(Scene):
|
|||
added_anims = [],
|
||||
**kwargs
|
||||
):
|
||||
target_point = self.camera.spherical_coords_to_point(phi, theta, distance)
|
||||
target_point = self.camera.get_spherical_coords(phi, theta, distance)
|
||||
movement = ApplyMethod(
|
||||
self.camera.position_mobject.move_to,
|
||||
self.camera.rotation_mobject.move_to,
|
||||
target_point,
|
||||
**kwargs
|
||||
)
|
||||
|
|
@ -175,7 +173,7 @@ class ThreeDScene(Scene):
|
|||
|
||||
def separate_moving_and_static_mobjects(self, *animations):
|
||||
moving, static = Scene.separate_moving_and_static_mobjects(self, *animations)
|
||||
if self.camera.position_mobject in moving:
|
||||
if self.camera.rotation_mobject in moving:
|
||||
return moving + static, []
|
||||
return moving, static
|
||||
|
||||
|
|
|
|||
563
waves.py
563
waves.py
|
|
@ -108,12 +108,13 @@ class EMWave(ContinualAnimationGroup):
|
|||
"frequency" : 0.25,
|
||||
"n_vectors" : 40,
|
||||
"propogation_direction" : RIGHT,
|
||||
"start_point" : SPACE_WIDTH*LEFT,
|
||||
"start_point" : SPACE_WIDTH*LEFT + DOWN + OUT,
|
||||
"length" : 2*SPACE_WIDTH,
|
||||
"amplitude" : 1,
|
||||
"rotation" : 0,
|
||||
"A_vect" : [0, 0, 1],
|
||||
"phi_vect" : [0, 0, 0],
|
||||
"requires_start_up" : False,
|
||||
}
|
||||
def __init__(self, **kwargs):
|
||||
digest_config(self, kwargs)
|
||||
|
|
@ -162,6 +163,25 @@ class EMWave(ContinualAnimationGroup):
|
|||
ContinualAnimationGroup.__init__(self, *vector_oscillations)
|
||||
|
||||
def update_mobject(self, dt):
|
||||
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
|
||||
|
||||
ContinualAnimationGroup.update_mobject(self, dt)
|
||||
self.mobject.rotate(self.rotation, RIGHT)
|
||||
if self.matrix_transform:
|
||||
|
|
@ -273,54 +293,513 @@ class PolarizingFilter(Circle):
|
|||
self.add(arrow_label)
|
||||
self.arrow_label = arrow_label
|
||||
|
||||
class EMScene(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
################
|
||||
|
||||
class Test(ThreeDScene):
|
||||
class IntroduceElectricField(PiCreatureScene):
|
||||
CONFIG = {
|
||||
"vector_field_colors" : [BLUE_B, BLUE_D],
|
||||
"max_vector_length" : 0.9,
|
||||
}
|
||||
def construct(self):
|
||||
self.add(ThreeDAxes())
|
||||
self.write_title()
|
||||
self.draw_field()
|
||||
self.add_particle()
|
||||
self.let_particle_wander()
|
||||
|
||||
self.set_camera_position(0.8*np.pi/2, -0.6*np.pi)
|
||||
self.begin_ambient_camera_rotation(rate = 0.01)
|
||||
self.add(EMWave(A_vect = [0, 1, 1]))
|
||||
self.dither(2)
|
||||
self.move_camera(theta = -1.1*np.pi)
|
||||
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),
|
||||
)
|
||||
self.dither(2)
|
||||
|
||||
# pol_filter = PolarizingFilter(
|
||||
# label_tex = "C",
|
||||
# filter_angle = np.pi/4,
|
||||
# )
|
||||
# pol_filter.rotate(np.pi/2, RIGHT)
|
||||
# pol_filter.rotate(-np.pi/2, OUT)
|
||||
# pol_filter.shift(DOWN+OUT)
|
||||
# pol_filter.arrow_label.rotate_in_place(np.pi/4, OUT)
|
||||
# shade_in_3d(pol_filter)
|
||||
# self.add(pol_filter)
|
||||
|
||||
# photon = WavePacket(
|
||||
# run_time = 2,
|
||||
# # get_filtered = True,
|
||||
# EMWave_config = {
|
||||
# "start_point" : SPACE_WIDTH*LEFT + DOWN+OUT,
|
||||
# "A_vect" : [0, 1, 0],
|
||||
# },
|
||||
# )
|
||||
# photon.update(0.5)
|
||||
# photon.mobject.show(self.camera)
|
||||
|
||||
# self.move_camera(theta = -1.2*np.pi/2)
|
||||
# self.play(photon)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
def get_vector(self, point):
|
||||
return Vector(self.field_function(point)).shift(point)
|
||||
|
||||
def field_function(self, point):
|
||||
x, y = point[:2]
|
||||
result = y*RIGHT + np.sin(x)*UP
|
||||
return self.normalized(result)
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
|
||||
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()
|
||||
|
||||
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)
|
||||
|
||||
self.dither(7)
|
||||
self.play(Write(words, run_time = 2))
|
||||
self.dither(20)
|
||||
|
||||
#####
|
||||
|
||||
class SimpleEMWave(IntroduceEMWave):
|
||||
def construct(self):
|
||||
self.dither(30)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue