mirror of
https://github.com/3b1b/manim.git
synced 2025-09-19 04:41:56 +00:00
Updates to light-bouncing clack solution
This commit is contained in:
parent
c8c003ba4b
commit
dc24c03349
6 changed files with 714 additions and 64 deletions
|
@ -2,6 +2,9 @@
|
|||
# from active_projects.clacks import solution1
|
||||
from active_projects.clacks.solution2 import block_collision_scenes
|
||||
from active_projects.clacks.solution2 import simple_scenes
|
||||
from active_projects.clacks.solution2 import wordy_scenes
|
||||
from active_projects.clacks.solution2 import pi_creature_scenes
|
||||
from active_projects.clacks.solution2 import position_phase_space
|
||||
|
||||
OUTPUT_DIRECTORY = "clacks_solution2"
|
||||
ALL_SCENE_CLASSES = [
|
||||
|
@ -12,4 +15,7 @@ ALL_SCENE_CLASSES = [
|
|||
block_collision_scenes.IntroducePreviousTwoVideos,
|
||||
block_collision_scenes.PreviousTwoVideos,
|
||||
simple_scenes.TwoSolutionsWrapper,
|
||||
wordy_scenes.ConnectionToOptics,
|
||||
pi_creature_scenes.OnAnsweringTwice,
|
||||
position_phase_space.IntroducePositionPhaseSpace,
|
||||
]
|
|
@ -1,6 +1,4 @@
|
|||
from big_ol_pile_of_manim_imports import *
|
||||
import subprocess
|
||||
from pydub import AudioSegment
|
||||
|
||||
|
||||
class Block(Square):
|
||||
|
@ -370,27 +368,6 @@ class BlocksAndWallScene(Scene):
|
|||
if self.include_sound:
|
||||
self.add_clack_sounds(self.clack_data)
|
||||
|
||||
# TODO, this no longer works
|
||||
# should use Scene.add_sound instead
|
||||
def combine_movie_files(self):
|
||||
Scene.combine_movie_files(self)
|
||||
if self.include_sound:
|
||||
sound_file_path = self.create_sound_file(self.clack_data)
|
||||
movie_path = self.get_movie_file_path()
|
||||
temp_path = self.get_movie_file_path(str(self) + "TempSound")
|
||||
commands = [
|
||||
"ffmpeg",
|
||||
"-i", movie_path,
|
||||
"-i", sound_file_path,
|
||||
"-c:v", "copy", "-c:a", "aac",
|
||||
'-loglevel', 'error',
|
||||
"-strict", "experimental",
|
||||
temp_path,
|
||||
]
|
||||
subprocess.call(commands)
|
||||
subprocess.call(["rm", sound_file_path])
|
||||
subprocess.call(["mv", temp_path, movie_path])
|
||||
|
||||
# Animated scenes
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,47 @@
|
|||
from big_ol_pile_of_manim_imports import *
|
||||
|
||||
|
||||
class NewSceneName(TeacherStudentsScene):
|
||||
class OnAnsweringTwice(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
pass
|
||||
question = TextMobject("Why $\\pi$?")
|
||||
question.move_to(self.screen)
|
||||
question.to_edge(UP)
|
||||
other_questions = VGroup(
|
||||
TextMobject("Frequency of collisions?"),
|
||||
TextMobject("Efficient simulation?"),
|
||||
TextMobject("Time until last collision?"),
|
||||
)
|
||||
for mob in other_questions:
|
||||
mob.move_to(self.hold_up_spot, DOWN)
|
||||
|
||||
self.add(question)
|
||||
|
||||
self.student_says(
|
||||
"But we already \\\\ solved it",
|
||||
bubble_kwargs={"direction": LEFT},
|
||||
target_mode="raise_left_hand",
|
||||
added_anims=[self.teacher.change, "thinking"]
|
||||
)
|
||||
self.change_student_modes("sassy", "angry")
|
||||
self.wait()
|
||||
self.play(
|
||||
RemovePiCreatureBubble(self.students[2]),
|
||||
self.get_student_changes("erm", "erm"),
|
||||
ApplyMethod(
|
||||
question.move_to, self.hold_up_spot, DOWN,
|
||||
path_arc=-90 * DEGREES,
|
||||
),
|
||||
self.teacher.change, "raise_right_hand",
|
||||
)
|
||||
shown_questions = VGroup(question)
|
||||
for oq in other_questions:
|
||||
self.play(
|
||||
shown_questions.shift, 0.85 * UP,
|
||||
FadeInFromDown(oq),
|
||||
self.get_student_changes(
|
||||
*["pondering"] * 3,
|
||||
look_at_arg=oq
|
||||
)
|
||||
)
|
||||
shown_questions.add(oq)
|
||||
self.wait(3)
|
||||
|
|
464
active_projects/clacks/solution2/position_phase_space.py
Normal file
464
active_projects/clacks/solution2/position_phase_space.py
Normal file
|
@ -0,0 +1,464 @@
|
|||
from big_ol_pile_of_manim_imports import *
|
||||
from active_projects.clacks.question import Block
|
||||
from active_projects.clacks.question import Wall
|
||||
|
||||
|
||||
class PositionPhaseSpaceScene(Scene):
|
||||
CONFIG = {
|
||||
"rescale_coordinates": True,
|
||||
"wall_x": -6,
|
||||
"wall_config": {
|
||||
"height": 1.6,
|
||||
"tick_spacing": 0.35,
|
||||
"tick_length": 0.2,
|
||||
},
|
||||
"wall_height": 1.5,
|
||||
"floor_y": -3.5,
|
||||
"block1_config": {
|
||||
"mass": 10,
|
||||
"distance": 7,
|
||||
"velocity": 1,
|
||||
"width": 1.6,
|
||||
},
|
||||
"block2_config": {
|
||||
"mass": 1,
|
||||
"distance": 4,
|
||||
},
|
||||
"axes_config": {
|
||||
"x_min": -0.5,
|
||||
"x_max": 31,
|
||||
"y_min": -0.5,
|
||||
"y_max": 10.5,
|
||||
"x_axis_config": {
|
||||
"unit_size": 0.4,
|
||||
"tick_frequency": 2,
|
||||
},
|
||||
"y_axis_config": {
|
||||
"unit_size": 0.4,
|
||||
"tick_frequency": 2,
|
||||
},
|
||||
},
|
||||
"axes_center": 5 * LEFT + 0.65 * DOWN,
|
||||
"ps_dot_config": {
|
||||
"fill_color": RED,
|
||||
"background_stroke_width": 1,
|
||||
"background_stroke_color": BLACK,
|
||||
"radius": 0.05,
|
||||
}
|
||||
}
|
||||
|
||||
def setup(self):
|
||||
self.all_items = [
|
||||
self.get_floor(),
|
||||
self.get_wall(),
|
||||
self.get_blocks(),
|
||||
self.get_axes(),
|
||||
self.get_phase_space_point(),
|
||||
self.get_phase_space_x_line(),
|
||||
self.get_phase_space_y_line(),
|
||||
self.get_phase_space_dot(),
|
||||
self.get_phase_space_d1_label(),
|
||||
self.get_phase_space_d2_label(),
|
||||
self.get_d1_brace(),
|
||||
self.get_d2_brace(),
|
||||
self.get_d1_label(),
|
||||
self.get_d2_label(),
|
||||
self.get_d1_eq_d2_line(),
|
||||
self.get_d1_eq_d2_label(),
|
||||
self.get_d2_eq_w2_line(),
|
||||
self.get_d2_eq_w2_label(),
|
||||
self.get_time_tracker(),
|
||||
]
|
||||
|
||||
def get_corner(self):
|
||||
return self.wall_x * RIGHT + self.floor_y * UP
|
||||
|
||||
def get_mass_ratio(self):
|
||||
return op.truediv(
|
||||
self.block1.mass,
|
||||
self.block2.mass,
|
||||
)
|
||||
|
||||
def d1_to_x(self, d1):
|
||||
if self.rescale_coordinates:
|
||||
d1 *= np.sqrt(self.block1.mass)
|
||||
return d1
|
||||
|
||||
def d2_to_y(self, d2):
|
||||
if self.rescale_coordinates:
|
||||
d2 *= np.sqrt(self.block2.mass)
|
||||
return d2
|
||||
|
||||
def ds_to_point(self, d1, d2):
|
||||
return self.axes.coords_to_point(
|
||||
self.d1_to_x(d1), self.d2_to_y(d2),
|
||||
)
|
||||
|
||||
def point_to_ds(self, point):
|
||||
x, y = self.axes.point_to_coords(point)
|
||||
if self.rescale_coordinates:
|
||||
x /= np.sqrt(self.block1.mass)
|
||||
y /= np.sqrt(self.block2.mass)
|
||||
return (x, y)
|
||||
|
||||
def get_d1(self):
|
||||
return self.get_ds()[0]
|
||||
|
||||
def get_d2(self):
|
||||
return self.get_ds()[1]
|
||||
|
||||
def get_ds(self):
|
||||
return self.point_to_ds(self.ps_point.get_location())
|
||||
|
||||
def tie_blocks_to_ps_point(self):
|
||||
def update_blocks(blocks):
|
||||
d1, d2 = self.point_to_ds(self.ps_point.get_location())
|
||||
b1, b2 = blocks
|
||||
corner = self.get_corner()
|
||||
b1.move_to(corner + d1 * RIGHT, DL)
|
||||
b2.move_to(corner + d2 * RIGHT, DR)
|
||||
self.blocks.add_updater(update_blocks)
|
||||
|
||||
def time_to_ds(self, time):
|
||||
# Deals in its own phase space, different
|
||||
# from the one displayed
|
||||
m1 = self.block1.mass
|
||||
m2 = self.block2.mass
|
||||
v1 = self.block1.velocity
|
||||
# v2 = self.block2.velocity
|
||||
d1 = self.block1_config["distance"]
|
||||
d2 = self.block2_config["distance"]
|
||||
w2 = self.block2.width
|
||||
ps_speed = np.sqrt(m1) * abs(v1)
|
||||
theta = np.arctan(np.sqrt(m2 / m1))
|
||||
|
||||
def ds_to_ps_point(d1, d2):
|
||||
return np.array([
|
||||
d1 * np.sqrt(m1),
|
||||
d2 * np.sqrt(m2),
|
||||
0
|
||||
])
|
||||
|
||||
def ps_point_to_ds(point):
|
||||
return (
|
||||
point[0] / np.sqrt(m1),
|
||||
point[1] / np.sqrt(m2),
|
||||
)
|
||||
|
||||
ps_point = ds_to_ps_point(d1, d2 + w2)
|
||||
wedge_corner = ds_to_ps_point(w2, w2)
|
||||
ps_point -= wedge_corner
|
||||
# Pass into the mirror worlds
|
||||
ps_point += time * ps_speed * LEFT
|
||||
# Reflect back to the real world
|
||||
angle = angle_of_vector(ps_point)
|
||||
n = int(angle / theta)
|
||||
if n % 2 == 0:
|
||||
ps_point = rotate_vector(ps_point, -n * theta)
|
||||
else:
|
||||
ps_point = rotate_vector(
|
||||
ps_point,
|
||||
-(n + 1) * theta,
|
||||
)
|
||||
ps_point[1] = abs(ps_point[1])
|
||||
ps_point += wedge_corner
|
||||
return ps_point_to_ds(ps_point)
|
||||
|
||||
def get_clack_flashes(self):
|
||||
pass # TODO
|
||||
|
||||
# Mobject getters
|
||||
def get_floor(self):
|
||||
floor = self.floor = Line(
|
||||
self.wall_x * RIGHT,
|
||||
FRAME_WIDTH * RIGHT / 2,
|
||||
stroke_color=WHITE,
|
||||
stroke_width=3,
|
||||
)
|
||||
floor.move_to(self.get_corner(), LEFT)
|
||||
return floor
|
||||
|
||||
def get_wall(self):
|
||||
wall = self.wall = Wall(**self.wall_config)
|
||||
wall.move_to(self.get_corner(), DR)
|
||||
return wall
|
||||
|
||||
def get_blocks(self):
|
||||
blocks = self.blocks = VGroup()
|
||||
for n in [1, 2]:
|
||||
config = getattr(self, "block{}_config".format(n))
|
||||
block = Block(**config)
|
||||
block.move_to(self.get_corner(), DL)
|
||||
block.shift(config["distance"] * RIGHT)
|
||||
block.label.move_to(block)
|
||||
block.label.set_fill(BLACK)
|
||||
block.label.set_stroke(WHITE, 3, background=True)
|
||||
self.blocks.add(block)
|
||||
self.block1, self.block2 = blocks
|
||||
return blocks
|
||||
|
||||
def get_axes(self):
|
||||
axes = self.axes = Axes(**self.axes_config)
|
||||
axes.set_stroke(LIGHT_GREY, 2)
|
||||
axes.shift(
|
||||
self.axes_center - axes.coords_to_point(0, 0)
|
||||
)
|
||||
axes.add(self.get_axes_labels(axes))
|
||||
return axes
|
||||
|
||||
def get_axes_labels(self, axes):
|
||||
x_label = TexMobject("x = ", "d_1")
|
||||
y_label = TexMobject("y = ", "d_2")
|
||||
labels = VGroup(x_label, y_label)
|
||||
if self.rescale_coordinates:
|
||||
additions = map(TexMobject, [
|
||||
"\\sqrt{m_1}", "\\sqrt{m_2}"
|
||||
])
|
||||
for label, addition in zip(labels, additions):
|
||||
addition.move_to(label[1], DL)
|
||||
label[1].next_to(
|
||||
addition, RIGHT, SMALL_BUFF,
|
||||
aligned_edge=DOWN
|
||||
)
|
||||
addition[2:].set_color(BLUE)
|
||||
label.add(addition)
|
||||
x_label.next_to(axes.x_axis.get_right(), DL, MED_SMALL_BUFF)
|
||||
y_label.next_to(axes.y_axis.get_top(), DR, MED_SMALL_BUFF)
|
||||
for label in labels:
|
||||
label.shift_onto_screen()
|
||||
return labels
|
||||
|
||||
def get_phase_space_point(self):
|
||||
ps_point = self.ps_point = VectorizedPoint()
|
||||
ps_point.move_to(self.ds_to_point(
|
||||
self.block1.distance,
|
||||
self.block2.distance + self.block2.width
|
||||
))
|
||||
self.tie_blocks_to_ps_point()
|
||||
return ps_point
|
||||
|
||||
def get_phase_space_x_line(self):
|
||||
def get_x_line():
|
||||
origin = self.axes.coords_to_point(0, 0)
|
||||
point = self.ps_point.get_location()
|
||||
y_axis_point = np.array(origin)
|
||||
y_axis_point[1] = point[1]
|
||||
return DashedLine(
|
||||
y_axis_point, point,
|
||||
color=GREEN,
|
||||
stroke_width=2,
|
||||
)
|
||||
self.x_line = updating_mobject_from_func(get_x_line)
|
||||
return self.x_line
|
||||
|
||||
def get_phase_space_y_line(self):
|
||||
def get_y_line():
|
||||
origin = self.axes.coords_to_point(0, 0)
|
||||
point = self.ps_point.get_location()
|
||||
x_axis_point = np.array(origin)
|
||||
x_axis_point[0] = point[0]
|
||||
return DashedLine(
|
||||
x_axis_point, point,
|
||||
color=RED,
|
||||
stroke_width=2,
|
||||
)
|
||||
self.y_line = updating_mobject_from_func(get_y_line)
|
||||
return self.y_line
|
||||
|
||||
def get_phase_space_dot(self):
|
||||
self.ps_dot = ps_dot = Dot(**self.ps_dot_config)
|
||||
ps_dot.add_updater(lambda m: m.move_to(self.ps_point))
|
||||
return ps_dot
|
||||
|
||||
def get_d_label(self, n, get_d):
|
||||
label = VGroup(
|
||||
TexMobject("d_{}".format(n), "="),
|
||||
DecimalNumber(),
|
||||
)
|
||||
color = GREEN if n == 1 else RED
|
||||
label[0].set_color_by_tex("d_", color)
|
||||
label.scale(0.7)
|
||||
|
||||
def update_value(label):
|
||||
lhs, rhs = label
|
||||
rhs.set_value(get_d())
|
||||
rhs.next_to(
|
||||
lhs, RIGHT, SMALL_BUFF,
|
||||
aligned_edge=DOWN,
|
||||
)
|
||||
label.add_updater(update_value)
|
||||
return label
|
||||
|
||||
def get_phase_space_d_label(self, n, get_d, line, vect):
|
||||
label = self.get_d_label(n, get_d)
|
||||
label.add_updater(
|
||||
lambda m: m.next_to(line, vect, SMALL_BUFF)
|
||||
)
|
||||
return label
|
||||
|
||||
def get_phase_space_d1_label(self):
|
||||
self.ps_d1_label = self.get_phase_space_d_label(
|
||||
1, self.get_d1, self.x_line, UP,
|
||||
)
|
||||
return self.ps_d1_label
|
||||
|
||||
def get_phase_space_d2_label(self):
|
||||
self.ps_d2_label = self.get_phase_space_d_label(
|
||||
2, self.get_d2, self.y_line, RIGHT,
|
||||
)
|
||||
return self.ps_d2_label
|
||||
|
||||
def get_d_brace(self, get_right_point):
|
||||
line = Line(LEFT, RIGHT).set_width(6)
|
||||
brace = Brace(line, UP)
|
||||
|
||||
def update_brace(brace):
|
||||
right_point = get_right_point()
|
||||
left_point = np.array(right_point)
|
||||
left_point[0] = self.wall_x
|
||||
line.put_start_and_end_on(left_point, right_point)
|
||||
brace.match_width(line, stretch=True)
|
||||
brace.next_to(line, UP, buff=0)
|
||||
|
||||
brace.add_updater(update_brace)
|
||||
return brace
|
||||
|
||||
def get_d1_brace(self):
|
||||
self.d1_brace = self.get_d_brace(
|
||||
lambda: self.block1.get_corner(UL)
|
||||
)
|
||||
return self.d1_brace
|
||||
|
||||
def get_d2_brace(self):
|
||||
self.d2_brace = self.get_d_brace(
|
||||
lambda: self.block2.get_corner(UR)
|
||||
)
|
||||
# self.flip_brace_nip()
|
||||
return self.d2_brace
|
||||
|
||||
def flip_brace_nip(self, brace):
|
||||
nip_index = (len(brace) // 2) - 1
|
||||
nip = brace[nip_index:nip_index + 2]
|
||||
rect = brace[nip_index - 1]
|
||||
center = rect.get_center()
|
||||
center[0] = nip.get_center()[0]
|
||||
nip.rotate(PI, about_point=center)
|
||||
|
||||
def get_brace_d_label(self, n, get_d, brace, vect):
|
||||
label = self.get_d_label(n, get_d)
|
||||
label.add_updater(
|
||||
lambda m: m.next_to(brace, vect, 0)
|
||||
)
|
||||
return label
|
||||
|
||||
def get_d1_label(self):
|
||||
self.d1_label = self.get_brace_d_label(
|
||||
1, self.get_d1, self.d1_brace, UP
|
||||
)
|
||||
return self.d1_label
|
||||
|
||||
def get_d2_label(self):
|
||||
self.d2_label = self.get_brace_d_label(
|
||||
2, self.get_d2, self.d2_brace, UP
|
||||
)
|
||||
return self.d2_label
|
||||
|
||||
def get_d1_eq_d2_line(self):
|
||||
start = self.ds_to_point(0, 0)
|
||||
end = self.ds_to_point(15, 15)
|
||||
self.d1_eq_d2_line = DashedLine(start, end)
|
||||
self.d1_eq_d2_line.set_stroke(WHITE, 2)
|
||||
return self.d1_eq_d2_line
|
||||
|
||||
def get_d1_eq_d2_label(self):
|
||||
label = TexMobject("d1 = d2")
|
||||
label.scale(0.75)
|
||||
line = self.d1_eq_d2_line
|
||||
for i in range(len(line)):
|
||||
if line[i].get_top()[1] > FRAME_HEIGHT / 2:
|
||||
break
|
||||
label.next_to(line[i - 3], DR, SMALL_BUFF)
|
||||
self.d1_eq_d2_label = label
|
||||
return label
|
||||
|
||||
def get_d2_eq_w2_line(self):
|
||||
w2 = self.block2.width
|
||||
start = self.ds_to_point(0, w2)
|
||||
end = self.ds_to_point(30, w2)
|
||||
self.d2_eq_w2_line = DashedLine(start, end)
|
||||
self.d2_eq_w2_line.set_stroke(WHITE, 2)
|
||||
return self.d2_eq_w2_line
|
||||
|
||||
def get_d2_eq_w2_label(self):
|
||||
label = TexMobject("d2 = \\text{block width}")
|
||||
label.scale(0.75)
|
||||
label.next_to(self.d2_eq_w2_line, UP, SMALL_BUFF)
|
||||
label.to_edge(RIGHT, buff=MED_SMALL_BUFF)
|
||||
self.d2_eq_w_label = label
|
||||
return label
|
||||
|
||||
def get_time_tracker(self):
|
||||
time_tracker = self.time_tracker = ValueTracker(0)
|
||||
time_tracker.add_updater(
|
||||
lambda m, dt: m.increment_value(dt)
|
||||
)
|
||||
self.get_time = time_tracker.get_value
|
||||
self.add(time_tracker)
|
||||
return time_tracker
|
||||
|
||||
|
||||
class IntroducePositionPhaseSpace(PositionPhaseSpaceScene):
|
||||
CONFIG = {
|
||||
"rescale_coordinates": False,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
self.show_coordinates()
|
||||
self.show_xy_line()
|
||||
self.let_process_play_out()
|
||||
|
||||
self.add(*self.all_items)
|
||||
self.ps_point.add_updater(
|
||||
lambda m: m.move_to(self.ds_to_point(
|
||||
*self.time_to_ds(self.get_time())
|
||||
))
|
||||
)
|
||||
self.wait(10)
|
||||
# self.play(Rotating(
|
||||
# self.ps_point,
|
||||
# about_point=self.ps_point.get_location() + RIGHT,
|
||||
# run_time=3
|
||||
# ))
|
||||
|
||||
def show_coordinates(self):
|
||||
pass
|
||||
|
||||
def show_xy_line(self):
|
||||
pass
|
||||
|
||||
def let_process_play_out(self):
|
||||
pass
|
||||
|
||||
|
||||
class EqualMassCase(IntroducePositionPhaseSpace):
|
||||
def construct(self):
|
||||
self.show_first_point()
|
||||
self.up_to_first_collision()
|
||||
self.ask_about_momentum_transfer()
|
||||
self.up_to_second_collision()
|
||||
self.up_to_third_collision()
|
||||
|
||||
def show_first_point(self):
|
||||
pass
|
||||
|
||||
def up_to_first_collision(self):
|
||||
pass
|
||||
|
||||
def ask_about_momentum_transfer(self):
|
||||
pass
|
||||
|
||||
def up_to_second_collision(self):
|
||||
pass
|
||||
|
||||
def up_to_third_collision(self):
|
||||
pass
|
|
@ -7,42 +7,3 @@ class TwoSolutionsWrapper(Scene):
|
|||
|
||||
def construct(self):
|
||||
pass
|
||||
|
||||
|
||||
class ConnectionToOptics(Scene):
|
||||
def construct(self):
|
||||
e_group, m_group = k_groups = self.get_kinematics_groups()
|
||||
|
||||
self.add(k_groups)
|
||||
|
||||
def get_kinematics_groups(self):
|
||||
tex_to_color_map = {
|
||||
"m_1": BLUE,
|
||||
"m_2": BLUE,
|
||||
"v_1": RED,
|
||||
"v_2": RED,
|
||||
}
|
||||
energy_eq = TexMobject(
|
||||
"\\frac{1}{2} m_1 (v_1)^2 + "
|
||||
"\\frac{1}{2} m_2 (v_2)^2 = "
|
||||
"\\text{const.}",
|
||||
tex_to_color_map=tex_to_color_map
|
||||
)
|
||||
momentum_eq = TexMobject(
|
||||
"m_1 v_1 + m_2 v_2 = \\text{const.}",
|
||||
tex_to_color_map=tex_to_color_map
|
||||
)
|
||||
energy_label = TextMobject(
|
||||
"Conservation of energy"
|
||||
)
|
||||
momentum_label = TextMobject(
|
||||
"Conservation of momentum"
|
||||
)
|
||||
energy_group = VGroup(energy_eq, energy_label)
|
||||
momentum_group = VGroup(momentum_eq, momentum_label)
|
||||
groups = VGroup(energy_group, momentum_group)
|
||||
for group in groups:
|
||||
group.arrange_submobjects(DOWN)
|
||||
groups.arrange_submobjects(DOWN, LARGE_BUFF)
|
||||
groups.to_edge(LEFT)
|
||||
return groups
|
||||
|
|
201
active_projects/clacks/solution2/wordy_scenes.py
Normal file
201
active_projects/clacks/solution2/wordy_scenes.py
Normal file
|
@ -0,0 +1,201 @@
|
|||
from big_ol_pile_of_manim_imports import *
|
||||
|
||||
|
||||
class ConnectionToOptics(Scene):
|
||||
def construct(self):
|
||||
e_group, m_group = k_groups = self.get_kinematics_groups()
|
||||
c_group, a_group = o_groups = self.get_optics_groups()
|
||||
arrows = VGroup()
|
||||
for g1, g2 in zip(k_groups, o_groups):
|
||||
g2.align_to(g1, UP)
|
||||
g2.to_edge(RIGHT)
|
||||
arrow = TexMobject("\\Rightarrow")
|
||||
arrow.scale(1.5)
|
||||
arrow.move_to(interpolate(
|
||||
g1[0].get_right(), g2[0].get_left(), 0.5
|
||||
))
|
||||
arrows.add(arrow)
|
||||
everything = VGroup(k_groups, arrows, o_groups)
|
||||
everything.to_edge(UP)
|
||||
|
||||
everything.generate_target()
|
||||
everything.target.scale(0.9)
|
||||
everything.target.to_edge(DOWN)
|
||||
width = max([m.get_width() for m in everything.target])
|
||||
width += 2 * MED_SMALL_BUFF
|
||||
rects = VGroup()
|
||||
for k in [0, 2]:
|
||||
rect = DashedMobject(Rectangle(
|
||||
height=FRAME_HEIGHT - 1.5,
|
||||
width=width
|
||||
), dashes_num=100)
|
||||
rect.move_to(everything.target[k])
|
||||
rect.to_edge(DOWN, buff=SMALL_BUFF)
|
||||
rects.add(rect)
|
||||
titles = VGroup(
|
||||
TextMobject("Kinematics"),
|
||||
TextMobject("Optics"),
|
||||
)
|
||||
titles.scale(1.5)
|
||||
for title, rect in zip(titles, rects):
|
||||
title.next_to(rect, UP)
|
||||
titles[0].align_to(titles[1], UP)
|
||||
|
||||
self.play(FadeInFromDown(e_group))
|
||||
self.play(
|
||||
Write(arrows[0]),
|
||||
FadeInFrom(c_group, LEFT)
|
||||
)
|
||||
self.wait()
|
||||
self.play(FadeInFromDown(m_group))
|
||||
self.play(
|
||||
Write(arrows[1]),
|
||||
FadeInFrom(a_group, LEFT)
|
||||
)
|
||||
self.wait(4)
|
||||
for k in range(2):
|
||||
anims = [
|
||||
ShowCreation(rects[k]),
|
||||
FadeInFromDown(titles[k]),
|
||||
]
|
||||
if k == 0:
|
||||
anims.append(MoveToTarget(everything))
|
||||
self.play(*anims)
|
||||
self.wait()
|
||||
self.wait()
|
||||
self.wait(4)
|
||||
|
||||
def get_kinematics_groups(self):
|
||||
tex_to_color_map = {
|
||||
"m_1": BLUE,
|
||||
"m_2": BLUE,
|
||||
"v_1": RED,
|
||||
"v_2": RED,
|
||||
}
|
||||
energy_eq = TexMobject(
|
||||
"\\frac{1}{2} m_1 (v_1)^2 + "
|
||||
"\\frac{1}{2} m_2 (v_2)^2 = "
|
||||
"\\text{const.}",
|
||||
tex_to_color_map=tex_to_color_map
|
||||
)
|
||||
energy_eq.scale(0.8)
|
||||
momentum_eq = TexMobject(
|
||||
"m_1 v_1 + m_2 v_2 = \\text{const.}",
|
||||
tex_to_color_map=tex_to_color_map
|
||||
)
|
||||
energy_label = TextMobject(
|
||||
"Conservation of energy"
|
||||
)
|
||||
momentum_label = TextMobject(
|
||||
"Conservation of momentum"
|
||||
)
|
||||
energy_group = VGroup(energy_label, energy_eq)
|
||||
momentum_group = VGroup(momentum_label, momentum_eq)
|
||||
groups = VGroup(energy_group, momentum_group)
|
||||
for group in groups:
|
||||
group.arrange_submobjects(DOWN, buff=MED_LARGE_BUFF)
|
||||
group[0].set_color(GREEN)
|
||||
groups.arrange_submobjects(DOWN, buff=2)
|
||||
groups.to_edge(LEFT)
|
||||
return groups
|
||||
|
||||
def get_optics_groups(self):
|
||||
self.time_tracker = ValueTracker(0)
|
||||
self.time_tracker.add_updater(
|
||||
lambda m, dt: m.increment_value(dt)
|
||||
)
|
||||
self.add(self.time_tracker)
|
||||
return VGroup(
|
||||
self.get_speed_group(),
|
||||
self.get_angle_group()
|
||||
)
|
||||
|
||||
def get_speed_group(self):
|
||||
speed_label = TextMobject("Constant speed of light")
|
||||
speed_label.set_color(YELLOW)
|
||||
speed_light_template = Line(LEFT, RIGHT)
|
||||
speed_light_template.fade(1)
|
||||
speed_light_template.match_width(speed_label)
|
||||
speed_light_template.next_to(speed_label, DOWN, MED_LARGE_BUFF)
|
||||
speed_light = speed_light_template.deepcopy()
|
||||
|
||||
def update_speed_light(light, period=2, time_width=0.05):
|
||||
time = self.time_tracker.get_value()
|
||||
alpha = (time / period) % 1
|
||||
# alpha = 1 - 2 * abs(alpha - 0.5)
|
||||
alpha *= 1.5
|
||||
a = alpha - time_width / 2
|
||||
b = alpha + time_width / 2
|
||||
light.pointwise_become_partial(
|
||||
speed_light_template, max(a, 0), min(b, 1)
|
||||
)
|
||||
opacity = speed_label.family_members_with_points()[0].get_fill_opacity()
|
||||
light.set_stroke(YELLOW, width=3, opacity=opacity)
|
||||
# light.stretch(0.5, 0)
|
||||
# point = speed_light_template.point_from_proportion(0.25)
|
||||
# light.stretch(2, 0, about_point=point)
|
||||
|
||||
speed_light.add_updater(update_speed_light)
|
||||
result = VGroup(
|
||||
speed_label, speed_light_template, speed_light
|
||||
)
|
||||
return result
|
||||
|
||||
def get_angle_group(self):
|
||||
title = VGroup(*map(TextMobject, [
|
||||
"Angle of\\\\Incidence",
|
||||
"=",
|
||||
"Angle of\\\\Refraction",
|
||||
])).arrange_submobjects(RIGHT)
|
||||
title.set_color(YELLOW)
|
||||
h_line = Line(LEFT, RIGHT)
|
||||
h_line.match_width(title)
|
||||
h_line.set_stroke(LIGHT_GREY)
|
||||
h_line.set_sheen(1, UL)
|
||||
points = [
|
||||
h_line.get_left() + UP,
|
||||
h_line.get_center(),
|
||||
h_line.get_right() + UP,
|
||||
]
|
||||
dashed_lines = VGroup(
|
||||
DashedLine(*points[0:2]), DashedLine(*points[1:3])
|
||||
)
|
||||
dashed_lines.set_stroke(WHITE, 2)
|
||||
v_shape = VMobject()
|
||||
v_shape.set_points_as_corners(points)
|
||||
v_shape.fade(1)
|
||||
|
||||
theta = dashed_lines[1].get_angle()
|
||||
arcs = VGroup(
|
||||
Arc(start_angle=0, angle=theta),
|
||||
Arc(start_angle=PI, angle=-theta),
|
||||
)
|
||||
arcs.set_stroke(WHITE, 2)
|
||||
thetas = VGroup()
|
||||
for v in LEFT, RIGHT:
|
||||
theta = TexMobject("\\theta")
|
||||
theta.next_to(arcs, v, aligned_edge=DOWN)
|
||||
theta.shift(SMALL_BUFF * UP)
|
||||
thetas.add(theta)
|
||||
|
||||
beam = VMobject()
|
||||
|
||||
def update_beam(beam, period=2, time_width=0.05):
|
||||
time = self.time_tracker.get_value()
|
||||
alpha = (time / period) % 1
|
||||
alpha *= 1.5
|
||||
a = alpha - time_width / 2
|
||||
b = alpha + time_width / 2
|
||||
beam.pointwise_become_partial(
|
||||
v_shape, max(a, 0), min(b, 1)
|
||||
)
|
||||
opacity = title.family_members_with_points()[0].get_fill_opacity()
|
||||
beam.set_stroke(YELLOW, width=3, opacity=opacity)
|
||||
|
||||
beam.add_updater(update_beam)
|
||||
title.next_to(v_shape, UP, MED_LARGE_BUFF)
|
||||
|
||||
return VGroup(
|
||||
title, h_line, arcs, thetas,
|
||||
dashed_lines, v_shape, beam
|
||||
)
|
Loading…
Add table
Reference in a new issue