Merge pull request #426 from 3b1b/clacks

Clacks
This commit is contained in:
Grant Sanderson 2019-02-03 12:20:25 -08:00 committed by GitHub
commit 3b088b1284
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 3272 additions and 170 deletions

View file

@ -1,28 +0,0 @@
# from active_projects.clacks import question
# 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 = [
block_collision_scenes.IntroducePreviousTwoVideos,
block_collision_scenes.PreviousTwoVideos,
wordy_scenes.ConnectionToOptics,
pi_creature_scenes.OnAnsweringTwice,
simple_scenes.LastVideoWrapper,
position_phase_space.IntroducePositionPhaseSpace,
position_phase_space.UnscaledPositionPhaseSpaceMass100,
position_phase_space.EqualMassCase,
pi_creature_scenes.AskAboutEqualMassMomentumTransfer,
position_phase_space.FailedAngleRelation,
position_phase_space.UnscaledPositionPhaseSpaceMass10,
pi_creature_scenes.ComplainAboutRelevanceOfAnalogy,
simple_scenes.LastVideoWrapper,
position_phase_space.RescaleCoordinates,
wordy_scenes.ConnectionToOpticsTransparent,
position_phase_space.RescaleCoordinatesMass16,
position_phase_space.RescaleCoordinatesMass64,
]

View file

@ -1,15 +0,0 @@
from big_ol_pile_of_manim_imports import *
class LastVideoWrapper(Scene):
def construct(self):
title = TextMobject("Last time...")
title.scale(1.5)
title.to_edge(UP)
rect = ScreenRectangle(height=6)
rect.next_to(title, DOWN)
self.play(
FadeInFromDown(title),
ShowCreation(rect)
)
self.wait()

View file

@ -29,6 +29,9 @@ class Animation(object):
mobject = instantiate(mobject)
assert(isinstance(mobject, Mobject))
digest_config(self, kwargs, locals())
# Make sure it's all up to date
mobject.update()
# Keep track of where it started
self.starting_mobject = self.mobject.copy()
if self.rate_func is None:
self.rate_func = (lambda x: x)

View file

@ -5,7 +5,6 @@ from manimlib.animation.transform import Transform
from manimlib.constants import *
from manimlib.mobject.svg.tex_mobject import TextMobject
from manimlib.mobject.types.vectorized_mobject import VMobject
from manimlib.mobject.types.vectorized_mobject import VectorizedPoint
from manimlib.utils.bezier import interpolate
from manimlib.utils.config_ops import digest_config
from manimlib.utils.paths import counterclockwise_path

View file

@ -44,7 +44,7 @@ class Rotate(Transform):
if "path_arc_axis" not in kwargs:
kwargs["path_arc_axis"] = axis
digest_config(self, kwargs, locals())
target = mobject.copy()
target = mobject.deepcopy()
if self.in_place:
self.about_point = mobject.get_center()
target.rotate(

View file

@ -29,8 +29,10 @@ class Transform(Animation):
# Copy target_mobject so as to not mess with caller
self.original_target_mobject = target_mobject
target_mobject = target_mobject.copy()
mobject.align_data(target_mobject)
target_mobject.update()
self.target_mobject = target_mobject
mobject.align_data(target_mobject)
digest_config(self, kwargs)
self.init_path_func()

View file

@ -30,7 +30,7 @@ class Camera(object):
"background_image": None,
"pixel_height": DEFAULT_PIXEL_HEIGHT,
"pixel_width": DEFAULT_PIXEL_WIDTH,
"frame_duration": DEFAULT_FRAME_DURATION,
"frame_rate": DEFAULT_FRAME_RATE,
# Note: frame height and width will be resized to match
# the pixel aspect ratio
"frame_height": FRAME_HEIGHT,

View file

@ -109,7 +109,7 @@ LOW_QUALITY_CAMERA_CONFIG = {
DEFAULT_PIXEL_HEIGHT = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_height"]
DEFAULT_PIXEL_WIDTH = PRODUCTION_QUALITY_CAMERA_CONFIG["pixel_width"]
DEFAULT_FRAME_DURATION = 30
DEFAULT_FRAME_RATE = 60
DEFAULT_POINT_DENSITY_2D = 25
DEFAULT_POINT_DENSITY_1D = 250

View file

@ -307,6 +307,7 @@ class Banner(Scene):
"date": "Sunday, February 3rd",
"message_scale_val": 0.9,
"add_supporter_note": False,
"pre_date_text": "Next video on ",
}
def __init__(self, **kwargs):
@ -363,7 +364,8 @@ class Banner(Scene):
def get_date_message(self):
return TextMobject(
"Next video on ", self.date,
self.pre_date_text,
self.date,
tex_to_color_map={self.date: YELLOW},
)

View file

@ -403,10 +403,11 @@ class Line(VMobject):
start, end = self.get_start_and_end()
return angle_of_vector(end - start)
# def put_start_and_end_on(self, new_start, new_end):
# self.set_start_and_end(new_start, new_end)
# self.buff = 0
# self.generate_points()
def set_angle(self, angle):
self.rotate(
angle - self.get_angle(),
about_point=self.get_start(),
)
def put_start_and_end_on(self, new_start, new_end):
self.start = new_start

View file

@ -125,6 +125,7 @@ class Mobject(Container):
copy_mobject.submobjects = [
submob.copy() for submob in self.submobjects
]
copy_mobject.updaters = list(self.updaters)
family = self.get_family()
for attr, value in list(self.__dict__.items()):
if isinstance(value, Mobject) and value in family and value is not self:
@ -147,13 +148,14 @@ class Mobject(Container):
# Updating
def update(self, dt=0, recursive=True):
if not self.updating_suspended:
for updater in self.updaters:
parameters = get_parameters(updater)
if "dt" in parameters:
updater(self, dt)
else:
updater(self)
if self.updating_suspended:
return self
for updater in self.updaters:
parameters = get_parameters(updater)
if "dt" in parameters:
updater(self, dt)
else:
updater(self)
if recursive:
for submob in self.submobjects:
submob.update(dt, recursive)
@ -182,8 +184,11 @@ class Mobject(Container):
self.updaters.remove(update_function)
return self
def clear_updaters(self):
def clear_updaters(self, recursive=True):
self.updaters = []
if recursive:
for submob in self.submobjects:
submob.clear_updaters()
return self
def suspend_updating(self, recursive=True):

View file

@ -32,9 +32,9 @@ class VMobject(Mobject):
"background_stroke_width": 0,
# When a color c is set, there will be a second color
# computed based on interpolating c to WHITE by with
# sheen, and the display will gradient to this
# sheen_factor, and the display will gradient to this
# secondary color in the direction of sheen_direction.
"sheen": 0.0,
"sheen_factor": 0.0,
"sheen_direction": UL,
# Indicates that it will not be displayed, but
# that it should count in parent mobject's path
@ -71,7 +71,7 @@ class VMobject(Mobject):
family=self.propagate_style_to_family,
)
self.set_sheen(
factor=self.sheen,
factor=self.sheen_factor,
direction=self.sheen_direction,
family=self.propagate_style_to_family
)
@ -81,7 +81,7 @@ class VMobject(Mobject):
"""
First arg can be either a color, or a tuple/list of colors.
Likewise, opacity can either be a float, or a tuple of floats.
If self.sheen is not zero, and only
If self.sheen_factor is not zero, and only
one color was passed in, a second slightly light color
will automatically be added for the gradient
"""
@ -92,10 +92,10 @@ class VMobject(Mobject):
for c, o in zip(*make_even(colors, opacities))
])
sheen = self.get_sheen()
if sheen != 0 and len(rgbas) == 1:
sheen_factor = self.get_sheen_factor()
if sheen_factor != 0 and len(rgbas) == 1:
light_rgbas = np.array(rgbas)
light_rgbas[:, :3] += sheen
light_rgbas[:, :3] += sheen_factor
clip_in_place(light_rgbas, 0, 1)
rgbas = np.append(rgbas, light_rgbas, axis=0)
return rgbas
@ -160,8 +160,10 @@ class VMobject(Mobject):
fill_opacity=None,
stroke_color=None,
stroke_width=None,
stroke_opacity=None,
background_stroke_color=None,
background_stroke_width=None,
background_stroke_opacity=None,
sheen_factor=None,
sheen_direction=None,
background_image_file=None,
@ -174,11 +176,13 @@ class VMobject(Mobject):
self.set_stroke(
color=stroke_color,
width=stroke_width,
opacity=stroke_opacity,
family=family,
)
self.set_background_stroke(
color=background_stroke_color,
width=background_stroke_width,
opacity=background_stroke_opacity,
family=family,
)
if sheen_factor:
@ -198,7 +202,7 @@ class VMobject(Mobject):
"stroke_width": self.get_stroke_width(),
"background_stroke_color": self.get_stroke_colors(background=True),
"background_stroke_width": self.get_stroke_width(background=True),
"sheen_factor": self.get_sheen(),
"sheen_factor": self.get_sheen_factor(),
"sheen_direction": self.get_sheen_direction(),
"background_image_file": self.get_background_image_file(),
}
@ -309,12 +313,12 @@ class VMobject(Mobject):
if family:
for submob in self.submobjects:
submob.set_sheen(factor, direction, family)
self.sheen = factor
self.sheen_factor = factor
if direction is not None:
# family set to false because recursion will
# already be handled above
self.set_sheen_direction(direction, family=False)
# Reset color to put sheen into effect
# Reset color to put sheen_factor into effect
if factor != 0:
self.set_stroke(self.get_stroke_color(), family=family)
self.set_fill(self.get_fill_color(), family=family)
@ -323,8 +327,8 @@ class VMobject(Mobject):
def get_sheen_direction(self):
return np.array(self.sheen_direction)
def get_sheen(self):
return self.sheen
def get_sheen_factor(self):
return self.sheen_factor
def get_gradient_start_and_end_points(self):
if self.shade_in_3d:
@ -613,7 +617,7 @@ class VMobject(Mobject):
"stroke_width",
"background_stroke_width",
"sheen_direction",
"sheen",
"sheen_factor",
]
for attr in attrs:
setattr(self, attr, interpolate(
@ -694,7 +698,7 @@ class VectorizedPoint(VMobject):
return self.artificial_height
def get_location(self):
return self.points[0]
return np.array(self.points[0])
def set_location(self, new_loc):
self.set_points(np.array([new_loc]))

View file

@ -528,6 +528,7 @@ class Scene(Container):
@handle_play_like_call
def wait(self, duration=DEFAULT_WAIT_TIME, stop_condition=None):
dt = 1 / self.camera.frame_rate
self.continual_update(dt=0) # Any problems with this?
if self.should_continually_update():
time_progression = self.get_wait_time_progression(duration, stop_condition)
for t in time_progression:

View file

@ -0,0 +1,62 @@
from old_projects.clacks import question
from old_projects.clacks.solution2 import block_collision_scenes
from old_projects.clacks.solution2 import mirror_scenes
from old_projects.clacks.solution2 import pi_creature_scenes
from old_projects.clacks.solution2 import position_phase_space
from old_projects.clacks.solution2 import simple_scenes
from old_projects.clacks.solution2 import wordy_scenes
OUTPUT_DIRECTORY = "clacks_solution2"
ALL_SCENE_CLASSES = [
question.NameIntro,
block_collision_scenes.IntroducePreviousTwoVideos,
block_collision_scenes.PreviousTwoVideos,
simple_scenes.ComingUpWrapper,
wordy_scenes.ConnectionToOptics,
pi_creature_scenes.OnAnsweringTwice,
simple_scenes.LastVideoWrapper,
simple_scenes.Rectangle,
simple_scenes.ShowRectangleCreation,
simple_scenes.LeftEdge,
simple_scenes.RightEdge,
position_phase_space.IntroducePositionPhaseSpace,
position_phase_space.UnscaledPositionPhaseSpaceMass100,
simple_scenes.FourtyFiveDegreeLine,
position_phase_space.EqualMassCase,
pi_creature_scenes.AskAboutEqualMassMomentumTransfer,
position_phase_space.FailedAngleRelation,
position_phase_space.UnscaledPositionPhaseSpaceMass10,
pi_creature_scenes.ComplainAboutRelevanceOfAnalogy,
simple_scenes.LastVideoWrapper,
simple_scenes.NoteOnEnergyLostToSound,
position_phase_space.RescaleCoordinates,
wordy_scenes.ConnectionToOpticsTransparent,
position_phase_space.RescaleCoordinatesMass16,
position_phase_space.RescaleCoordinatesMass64,
position_phase_space.RescaleCoordinatesMass100,
position_phase_space.IntroduceVelocityVector,
position_phase_space.IntroduceVelocityVectorWithoutZoom,
position_phase_space.ShowMomentumConservation,
wordy_scenes.RearrangeMomentumEquation,
simple_scenes.DotProductVideoWrapper,
simple_scenes.ShowDotProductMeaning,
position_phase_space.JustTheProcessNew,
mirror_scenes.ShowTrajectoryWithChangingTheta,
pi_creature_scenes.ReplaceOneTrickySceneWithAnother,
mirror_scenes.MirrorAndWiresOverlay,
pi_creature_scenes.NowForTheGoodPart,
mirror_scenes.ReflectWorldThroughMirrorNew,
mirror_scenes.ReflectWorldThroughMirrorThetaPoint2,
mirror_scenes.ReflectWorldThroughMirrorThetaPoint1,
simple_scenes.AskAboutAddingThetaToItself,
simple_scenes.AskAboutAddingThetaToItselfThetaPoint1,
simple_scenes.AskAboutAddingThetaToItselfThetaPoint2,
simple_scenes.FinalFormula,
simple_scenes.ArctanSqrtPoint1Angle,
simple_scenes.ReviewWrapper,
simple_scenes.SurprisedRandy,
simple_scenes.TwoSolutionsWrapper,
simple_scenes.FinalQuote,
simple_scenes.EndScreen,
simple_scenes.ClacksSolution2Thumbnail,
]

View file

@ -1,7 +1,6 @@
#!/usr/bin/env python
from big_ol_pile_of_manim_imports import *
from active_projects.clacks.question import BlocksAndWallExample
from old_projects.clacks.question import BlocksAndWallExample
class NameBump(BlocksAndWallExample):

View file

@ -13,7 +13,7 @@ class Block(Square):
"stroke_color": WHITE,
"fill_color": None,
"sheen_direction": UL,
"sheen": 0.5,
"sheen_factor": 0.5,
"sheen_direction": UL,
}
@ -43,7 +43,7 @@ class Block(Square):
def mass_to_color(self, mass):
colors = [
LIGHT_GREY,
BLUE_B,
BLUE_D,
BLUE_D,
BLUE_E,
BLUE_E,
@ -225,7 +225,7 @@ class ClackFlashes(ContinualAnimation):
ContinualAnimation.__init__(self, group, **kwargs)
def update_mobject(self, dt):
total_time = self.external_time
total_time = self.get_time()
group = self.mobject
for flash in self.flashes:
if flash.start_time < total_time < flash.end_time:
@ -238,6 +238,9 @@ class ClackFlashes(ContinualAnimation):
if flash.mobject in group:
group.remove(flash.mobject)
def get_time(self):
return self.external_time
class Wall(Line):
CONFIG = {
@ -410,6 +413,10 @@ class NameIntro(Scene):
rate_func=None,
)
)
self.play(
Flash(brown.get_right(), run_time=flash_time),
Restore(brown, rate_func=None)
)
class MathAndPhysicsConspiring(Scene):
@ -1558,16 +1565,16 @@ class Thumbnail(BlocksAndWallExample, MovingCameraScene):
BlocksAndWallExample.setup(self)
def construct(self):
# self.camera_frame.shift(0.9 * UP)
self.mobjects.insert(
0,
FullScreenFadeRectangle(
color=DARK_GREY,
opacity=0.5,
sheen_direction=UL,
sheen=0.5,
),
)
self.camera_frame.shift(0.9 * UP)
# self.mobjects.insert(
# 0,
# FullScreenFadeRectangle(
# color=DARK_GREY,
# opacity=0.5,
# sheen_direction=UL,
# sheen=0.5,
# ),
# )
self.thicken_lines()
self.grow_labels()
self.add_vector()
@ -1587,7 +1594,7 @@ class Thumbnail(BlocksAndWallExample, MovingCameraScene):
def add_vector(self):
blocks = self.blocks
arrow = Vector(
arrow = self.arrow = Vector(
2.5 * LEFT,
color=RED,
rectangular_stem_width=1.5,
@ -1600,7 +1607,9 @@ class Thumbnail(BlocksAndWallExample, MovingCameraScene):
self.add(arrow)
def add_text(self):
question = TextMobject("How many\\\\collisions?")
question = self.question = TextMobject(
"How many\\\\collisions?"
)
question.scale(2.5)
question.to_edge(UP)
question.set_color(YELLOW)

View file

@ -1,5 +1,5 @@
from big_ol_pile_of_manim_imports import *
from active_projects.clacks.question import *
from old_projects.clacks.question import *
from old_projects.div_curl import ShowTwoPopulations

View file

@ -1,5 +1,5 @@
from big_ol_pile_of_manim_imports import *
from active_projects.clacks.question import BlocksAndWallExample
from old_projects.clacks.question import BlocksAndWallExample
class PreviousTwoVideos(BlocksAndWallExample):
@ -7,7 +7,7 @@ class PreviousTwoVideos(BlocksAndWallExample):
"sliding_blocks_config": {
"block1_config": {
"mass": 1e2,
"velocity": -1,
"velocity": -2,
"width": 4,
"distance": 8,
},

File diff suppressed because it is too large Load diff

View file

@ -49,7 +49,16 @@ class OnAnsweringTwice(TeacherStudentsScene):
class AskAboutEqualMassMomentumTransfer(TeacherStudentsScene):
def construct(self):
pass
self.student_says("Why?")
self.change_student_modes("confused", "confused")
self.wait()
self.play(
RemovePiCreatureBubble(self.students[2]),
self.teacher.change, "raise_right_hand"
)
self.change_all_student_modes("pondering")
self.look_at(self.hold_up_spot + 2 * UP)
self.wait(5)
class ComplainAboutRelevanceOfAnalogy(TeacherStudentsScene):
@ -76,3 +85,34 @@ class ComplainAboutRelevanceOfAnalogy(TeacherStudentsScene):
self.hold_up_spot + UP,
)
self.wait(3)
class ReplaceOneTrickySceneWithAnother(TeacherStudentsScene):
def construct(self):
self.student_says(
"This replaces one tricky\\\\problem with another",
student_index=1,
target_mode="sassy",
added_anims=[self.teacher.change, "happy"],
)
self.change_student_modes("erm", "sassy", "angry")
self.wait(4)
self.play(
RemovePiCreatureBubble(self.students[1]),
self.teacher.change, "raise_right_hand",
self.get_student_changes(*3 * ["pondering"])
)
self.look_at(self.hold_up_spot + 2 * UP)
self.wait(5)
class NowForTheGoodPart(TeacherStudentsScene):
def construct(self):
self.teacher_says(
r"Now for the \\ good part!",
target_mode="hooray",
added_anims=[self.get_student_changes(
"hooray", "surprised", "happy"
)],
)
self.wait(2)

View file

@ -0,0 +1,841 @@
from big_ol_pile_of_manim_imports import *
from old_projects.lost_lecture import ShowWord
from old_projects.clacks.solution2.mirror_scenes import ReflectWorldThroughMirrorNew
from old_projects.clacks.question import Thumbnail
class WrapperScene(Scene):
CONFIG = {
"title": "Title",
"shade_of_grey": "#333333"
}
def construct(self):
title = TextMobject(self.title)
title.scale(1.5)
title.to_edge(UP)
big_rect = self.get_big_rect()
screen_rect = self.get_screen_rect()
screen_rect.next_to(title, DOWN)
self.add(big_rect, screen_rect)
self.play(
FadeIn(big_rect),
FadeInFrom(title, DOWN),
FadeInFrom(screen_rect, UP),
)
self.wait()
def get_big_rect(self):
big_rect = FullScreenFadeRectangle()
big_rect.set_fill(self.shade_of_grey, 1)
return big_rect
def get_screen_rect(self, height=6):
screen_rect = ScreenRectangle(height=height)
screen_rect.set_fill(BLACK, 1)
return screen_rect
class ComingUpWrapper(WrapperScene):
CONFIG = {"title": "Coming up..."}
class LastVideoWrapper(WrapperScene):
CONFIG = {"title": "Last time..."}
class LeftEdge(Scene):
CONFIG = {
"text": "Left edge",
"vect": LEFT,
}
def construct(self):
words = TextMobject(self.text)
arrow = Vector(self.vect)
arrow.match_width(words)
arrow.next_to(words, DOWN)
self.play(
FadeInFromDown(words),
GrowArrow(arrow)
)
self.wait()
class RightEdge(LeftEdge):
CONFIG = {
"text": "Right edge",
"vect": RIGHT,
}
class NoteOnEnergyLostToSound(Scene):
def construct(self):
self.add(TextMobject(
"Yeah yeah, the clack sound\\\\"
"would require energy, but\\\\"
"don't let accuracy get in the\\\\"
"way of delight!",
alignment="",
))
class DotProductVideoWrapper(WrapperScene):
CONFIG = {"title": "Dot product"}
class Rectangle(Scene):
def construct(self):
rect = ScreenRectangle(height=FRAME_HEIGHT - 0.25)
rect.set_stroke(WHITE, 6)
self.add(rect)
class ShowRectangleCreation(Scene):
def construct(self):
rect = ScreenRectangle(height=2)
rect.set_stroke(YELLOW, 6)
self.play(ShowCreation(rect))
self.play(FadeOut(rect))
class ShowDotProductMeaning(Scene):
def construct(self):
v_vect = Vector(2 * RIGHT, color=YELLOW)
w_vect = Vector(3 * RIGHT, color=PINK)
dot = Dot(color=RED)
dot.shift(DOWN)
v_vect.angle_tracker = ValueTracker()
w_vect.angle_tracker = ValueTracker()
def update_vect(vect):
target = vect.angle_tracker.get_value()
vect.rotate(target - vect.get_angle())
vect.shift(dot.get_center() - vect.get_start())
v_vect.add_updater(update_vect)
w_vect.add_updater(update_vect)
v_label = TexMobject("\\vec{\\textbf{v}}")
v_label.vect = v_vect
w_label = TexMobject("\\vec{\\textbf{w}}")
w_label.vect = w_vect
for label in v_label, w_label:
label.match_color(label.vect)
label.set_stroke(BLACK, 5, background=True)
def update_label(label):
target = np.array(label.vect.get_end())
target += 0.25 * normalize(label.vect.get_vector())
label.move_to(target)
v_label.add_updater(update_label)
w_label.add_updater(update_label)
title = TexMobject(
"\\vec{\\textbf{w}}",
"\\cdot",
"\\vec{\\textbf{v}}",
"=",
"||", "\\vec{\\textbf{w}}", "||",
"\\cdot",
"||", "\\vec{\\textbf{v}}", "||",
"\\cdot",
"\\cos(\\theta)"
)
title.set_color_by_tex_to_color_map({
"textbf{v}": v_vect.get_color(),
"textbf{w}": w_vect.get_color(),
})
title.to_edge(UP)
def get_w_line():
center = dot.get_center()
direction = w_vect.get_vector()
return Line(
center - 3 * direction,
center + 3 * direction,
stroke_color=LIGHT_GREY,
stroke_width=1,
)
w_line = updating_mobject_from_func(get_w_line)
def get_proj_v():
center = dot.get_center()
v = v_vect.get_vector()
w = w_vect.get_vector()
w_unit = normalize(w)
result = Vector(np.dot(v, w_unit) * w_unit)
result.set_fill(v_vect.get_color(), 0.5)
result.shift(center - result.get_start())
return result
proj_v = updating_mobject_from_func(get_proj_v)
def get_proj_line():
return DashedLine(
v_vect.get_end(),
proj_v.get_end(),
stroke_width=1,
dashed_segment_length=0.025,
)
proj_line = updating_mobject_from_func(get_proj_line)
template_line = Line(LEFT, RIGHT)
def get_vect_brace(vect):
brace = Brace(template_line, UP, buff=SMALL_BUFF)
brace.set_width(vect.get_length(), stretch=True)
angle = vect.get_angle() % TAU
if angle < PI:
angle += PI
brace.rotate(angle, about_point=ORIGIN)
brace.shift(vect.get_center())
return brace
w_brace = updating_mobject_from_func(
lambda: get_vect_brace(w_vect)
)
proj_v_brace = updating_mobject_from_func(
lambda: get_vect_brace(proj_v)
)
def get_arc():
center = dot.get_center()
a1 = w_vect.get_angle()
a2 = v_vect.get_angle()
arc = Arc(
start_angle=a1,
angle=a2 - a1,
radius=0.5,
arc_center=center,
)
theta = TexMobject("\\theta")
p = arc.point_from_proportion(0.5)
theta.move_to(
center + 1.5 * (p - center)
)
return VGroup(arc, theta)
arc = updating_mobject_from_func(get_arc)
self.add(
title[:3],
w_vect, v_vect, dot,
w_label, v_label,
)
self.play(
v_vect.angle_tracker.set_value, 170 * DEGREES,
w_vect.angle_tracker.set_value, 45 * DEGREES,
run_time=2,
)
self.wait()
w_brace.update()
self.play(
GrowFromCenter(w_brace),
Write(title[3:7])
)
self.add(w_line, w_vect, w_label, dot)
self.play(ShowCreation(w_line))
proj_v.update()
self.play(
ShowCreation(proj_line),
TransformFromCopy(v_vect, proj_v),
)
self.add(proj_v, proj_line, dot)
proj_v_brace.update()
self.play(
GrowFromCenter(proj_v_brace),
FadeInFromDown(title[7:])
)
arc.update()
self.play(Write(arc))
self.wait()
for angle in [135, 225, 270, 90, 150]:
self.play(
v_vect.angle_tracker.set_value, angle * DEGREES,
run_time=2
)
self.wait()
class FinalComment(Scene):
def construct(self):
self.add(TextMobject(
"Thoughts on what ending should go here?\\\\"
"See the Patreon post."
))
class FourtyFiveDegreeLine(Scene):
CONFIG = {
"angle": 45 * DEGREES,
"label_config": {
"num_decimal_places": 0,
"unit": "^\\circ",
"label_height": 0.3,
},
"degrees": True
}
def construct(self):
angle = self.angle
arc = Arc(angle, radius=1)
label = DecimalNumber(0, **self.label_config)
label.set_height(self.label_config["label_height"])
label.next_to(arc, RIGHT)
label.shift(0.5 * SMALL_BUFF * UP)
line1 = Line(ORIGIN, 3 * RIGHT)
line2 = line1.copy()
if self.degrees:
target_value = int(angle / DEGREES)
else:
target_value = angle
self.add(line1, label)
self.play(
ChangeDecimalToValue(label, target_value),
ShowCreation(arc),
Rotate(line2, angle, about_point=ORIGIN)
)
self.wait()
class ArctanSqrtPoint1Angle(FourtyFiveDegreeLine):
CONFIG = {
"angle": np.arctan(np.sqrt(0.1)),
}
class AskAboutAddingThetaToItself(Scene):
CONFIG = {
"theta": np.arctan(0.25),
"wait_time": 0.25,
"wedge_radius": 3,
"theta_symbol_scale_val": 0.5,
"number_height": 0.2,
}
def construct(self):
theta = self.theta
groups = self.get_groups(theta)
horizon = self.get_horizon()
counter = ValueTracker(0)
dynamic_ineq = self.get_dynamic_inequality(counter)
semicircle = self.get_semicircle()
self.add(horizon)
self.add(dynamic_ineq)
for n in range(len(groups)):
counter.set_value(n + 1)
if n < len(groups) - 1:
groups[n][-1].set_color(YELLOW)
if n > 0:
groups[n - 1][-1].set_color(WHITE)
self.add(groups[:n + 1])
self.add_sound("pen_click", gain=-20)
self.wait(self.wait_time)
self.wait(0.5)
counter.set_value(counter.get_value() - 1)
self.remove(groups[-1])
self.add_sound("pen_click", gain=-20)
self.wait()
self.play(ShowCreation(semicircle))
self.play(FadeOut(semicircle))
self.wait(3)
def get_group(self, theta):
# Define group
wedge_radius = self.wedge_radius
wedge = VGroup(
Line(ORIGIN, wedge_radius * RIGHT),
Line(ORIGIN, wedge_radius * RIGHT).rotate(
theta, about_point=ORIGIN
),
)
wedge.set_stroke((WHITE, GREY), 2)
arc = Arc(theta, radius=1)
theta_symbol = TexMobject("\\theta")
tssv = self.theta_symbol_scale_val
theta_symbol.scale(tssv)
theta_symbol.next_to(arc, RIGHT, tssv / 2)
theta_symbol.shift(tssv * SMALL_BUFF * UP)
return VGroup(wedge, arc, theta_symbol)
def get_groups(self, theta):
group = self.get_group(theta)
angles = [k * theta for k in range(int(PI / theta) + 1)]
groups = VGroup(*[
group.copy().rotate(angle, about_point=ORIGIN)
for angle in angles
])
# colors = it.cycle([BLUE_D, BLUE_B, BLUE_C, GREY_BROWN])
colors = it.cycle([BLUE_D, GREY_BROWN])
for n, angle, group, color in zip(it.count(1), angles, groups, colors):
wedge, arc, symbol = group
symbol.rotate(-angle)
arc.set_color(color)
number = Integer(n)
number.set_height(self.number_height)
number.move_to(center_of_mass([
wedge[0].get_end(),
wedge[1].get_end(),
]))
group.add(number)
groups[-1][-1].set_color(RED)
return groups
def get_horizon(self):
horizon = DashedLine(5 * LEFT, 5 * RIGHT)
horizon.set_stroke(WHITE, 1)
return horizon
def get_semicircle(self):
return Arc(
start_angle=0,
angle=PI,
radius=self.wedge_radius / 2,
color=YELLOW,
stroke_width=4,
)
def get_inequality(self):
ineq = TexMobject(
"N", "\\cdot", "\\theta", "<",
"\\pi", "=", "3.1415926\\dots"
)
N = ineq.get_part_by_tex("N")
self.pi_symbol = ineq.get_part_by_tex("\\pi")
N.set_color(YELLOW)
# ineq[-3:].set_color(BLUE)
brace = Brace(N, UP, buff=SMALL_BUFF)
text = brace.get_text("Maximum", buff=SMALL_BUFF)
group = VGroup(ineq, brace, text)
group.next_to(ORIGIN, DOWN, MED_LARGE_BUFF)
return group
def get_dynamic_inequality(self, counter):
multiple = Integer(0)
dot = TexMobject("\\cdot")
theta_tex = TexMobject("({:.2f})".format(self.theta))
eq = TexMobject("=")
value = DecimalNumber(0)
ineq = TexMobject("<")
pi = TexMobject("\\pi", "=", "3.1415926\\dots")
# pi.set_color(BLUE)
group = VGroup(
multiple, dot, theta_tex,
eq, value,
ineq, pi
)
group.arrange_submobjects(RIGHT, buff=0.2)
group.next_to(ORIGIN, DOWN, buff=LARGE_BUFF)
theta_brace = Brace(group[2], DOWN, buff=SMALL_BUFF)
theta_symbol = theta_brace.get_tex("\\theta")
group.add(theta_brace, theta_symbol)
# group.align_to(self.pi_symbol, RIGHT)
def get_count():
return int(counter.get_value())
def get_product():
return get_count() * self.theta
def is_greater_than_pi():
return get_product() > PI
def get_color():
return RED if is_greater_than_pi() else YELLOW
def get_ineq():
result = TexMobject(
">" if is_greater_than_pi() else "<"
)
result.set_color(get_color())
result.move_to(ineq)
return result
dynamic_ineq = updating_mobject_from_func(get_ineq)
group.remove(ineq)
group.add(dynamic_ineq)
multiple.add_updater(lambda m: m.set_value(get_count()))
multiple.add_updater(lambda m: m.next_to(dot, LEFT, 0.2))
multiple.add_updater(lambda m: m.set_color(get_color()))
value.add_updater(lambda m: m.set_value(get_product()))
return group
class AskAboutAddingThetaToItselfThetaPoint1(AskAboutAddingThetaToItself):
CONFIG = {
"theta": 0.1,
"wait_time": 0.1,
"theta_symbol_scale_val": 0.25,
"number_height": 0.15,
}
class AskAboutAddingThetaToItselfThetaPoint2(AskAboutAddingThetaToItself):
CONFIG = {
"theta": 0.2,
"wait_time": 0.1,
}
class FinalFormula(Scene):
def construct(self):
text = TextMobject("Final answer: ")
t2c_map = {
"\\theta": BLUE,
"m_1": GREEN,
"m_2": RED,
}
formula = TexMobject(
"\\left\\lfloor",
"{\\pi", "\\over", "\\theta}",
"\\right\\rfloor"
)
formula.set_color_by_tex_to_color_map(t2c_map)
group = VGroup(text, formula)
group.arrange_submobjects(RIGHT)
group.scale(1.5)
group.to_edge(UP)
self.play(Write(text))
self.play(FadeInFrom(formula))
self.play(ShowCreationThenFadeAround(formula))
self.wait()
theta_eq = TexMobject(
"\\theta", "=", "\\arctan", "\\left(",
"\\sqrt",
"{{m_2", "\\over", "m_1}}",
"\\right)"
)
theta_eq.set_color_by_tex_to_color_map(t2c_map)
theta_eq.scale(1.5)
theta_eq.next_to(group, DOWN, MED_LARGE_BUFF)
self.play(TransformFromCopy(
formula.get_part_by_tex("\\theta"),
theta_eq.get_part_by_tex("\\theta"),
))
self.play(Write(theta_eq[1:]))
self.wait()
class ReviewWrapper(WrapperScene):
CONFIG = {"title": "To review:"}
class SurprisedRandy(Scene):
def construct(self):
randy = Randolph()
self.play(randy.change, "surprised", 3 * UR)
self.play(Blink(randy))
self.play(randy.change, "confused")
self.play(Blink(randy))
self.wait()
class TwoSolutionsWrapper(WrapperScene):
def construct(self):
big_rect = self.get_big_rect()
screen_rects = VGroup(*[
self.get_screen_rect(height=3)
for x in range(2)
])
screen_rects.arrange_submobjects(RIGHT, buff=LARGE_BUFF)
title = TextMobject("Two solutions")
title.scale(1.5)
title.to_edge(UP)
screen_rects.next_to(title, DOWN, LARGE_BUFF)
# pi creatures
pis = VGroup(
Randolph(color=BLUE_D),
Randolph(color=BLUE_E),
Randolph(color=BLUE_B),
Mortimer().scale(1.2)
)
pis.set_height(2)
pis.arrange_submobjects(RIGHT, buff=MED_LARGE_BUFF)
pis.to_edge(DOWN, buff=SMALL_BUFF)
self.add(big_rect, title, pis)
self.play(
LaggedStart(
ShowCreation, screen_rects.copy().set_fill(opacity=0),
lag_ratio=0.8
),
LaggedStart(
FadeIn, screen_rects,
lag_ratio=0.8
),
LaggedStart(
ApplyMethod, pis,
lambda pi: (pi.change, "pondering", screen_rects[0])
),
)
self.play(Blink(random.choice(pis)))
self.play(LaggedStart(
ApplyMethod, pis,
lambda pi: (pi.change, "thinking", screen_rects[1])
))
self.play(Blink(random.choice(pis)))
self.wait()
class FinalQuote(Scene):
def construct(self):
quote_text = """
A change of perspective\\\\
is worth 80 IQ points.
"""
quote_parts = [s for s in quote_text.split(" ") if s]
quote = TextMobject(
*quote_parts,
)
quote.scale(1.2)
quote.shift(2 * RIGHT + UP)
image = ImageMobject("AlanKay")
image.set_height(6)
image.to_corner(UL)
image.shift(2 * LEFT + 0.5 * UP)
name = TextMobject("Alan Kay")
name.scale(1.5)
name.next_to(image, DOWN)
name.shift_onto_screen()
self.play(
FadeInFromDown(image),
Write(name),
)
self.wait()
for word in quote:
self.play(ShowWord(word))
self.wait(0.005 * len(word)**1.5)
self.wait()
class EndScreen(PatreonEndScreen):
CONFIG = {
"specific_patrons": [
"1stViewMaths",
"Adam Kozak",
"Adrian Robinson",
"Alexis Olson",
"Ali Yahya",
"Andreas Benjamin Brössel",
"Andrew Busey",
"Ankalagon",
"Antonio Juarez",
"Arjun Chakroborty",
"Art Ianuzzi",
"Arthur Zey",
"Awoo",
"Bernd Sing",
"Bob Sanderson",
"Boris Veselinovich",
"Brian Staroselsky",
"Britt Selvitelle",
"Burt Humburg",
"Chad Hurst",
"Charles Southerland",
"Chris Connett",
"Christian Kaiser",
"Clark Gaebel",
"Cooper Jones",
"D. Sivakumar",
"Danger Dai",
"Dave B",
"Dave Kester",
"dave nicponski",
"David Clark",
"David Gow",
"Delton Ding",
"Devarsh Desai",
"eaglle",
"emptymachine",
"Eric Younge",
"Eryq Ouithaqueue",
"Evan Phillips",
"Federico Lebron",
"Florian Chudigiewitsch",
"Giovanni Filippi",
"Graham",
"Hal Hildebrand",
"Hitoshi Yamauchi",
"J",
"j eduardo perez",
"Jacob Magnuson",
"Jameel Syed",
"James Hughes",
"Jan Pijpers",
"Jason Hise",
"Jeff Linse",
"Jeff Straathof",
"John Griffith",
"John Haley",
"John Shaughnessy",
"John V Wertheim",
"Jonathan Eppele",
"Jonathan Wilson",
"Jordan Scales",
"Joseph John Cox",
"Joseph Kelly",
"Juan Benet",
"Kai-Siang Ang",
"Kanan Gill",
"Kaustuv DeBiswas",
"L0j1k",
"Lee Redden",
"Linh Tran",
"Luc Ritchie",
"Ludwig Schubert",
"Lukas -krtek.net- Novy",
"Lukas Biewald",
"Magister Mugit",
"Magnus Dahlström",
"Magnus Lysfjord",
"Mark B Bahu",
"Mark Heising",
"Mathew Bramson",
"Mathias Jansson",
"Matt Langford",
"Matt Roveto",
"Matt Russell",
"Matthew Cocke",
"Mauricio Collares",
"Michael Faust",
"Michael Hardel",
"Mike Coleman",
"Mustafa Mahdi",
"Márton Vaitkus",
"Nathan Jessurun",
"Nero Li",
"Omar Zrien",
"Owen Campbell-Moore",
"Peter Ehrnstrom",
"Peter Mcinerney",
"Quantopian",
"Randy C. Will",
"Richard Barthel",
"Richard Burgmann",
"Richard Comish",
"Ripta Pasay",
"Rish Kundalia",
"Robert Teed",
"Roobie",
"Roy Larson",
"Ryan Atallah",
"Ryan Williams",
"Samuel D. Judge",
"Scott Gray",
"Scott Walter, Ph.D.",
"Sindre Reino Trosterud",
"soekul",
"Solara570",
"Song Gao",
"Stevie Metke",
"Ted Suzman",
"Tihan Seale",
"Valeriy Skobelev",
"Vassili Philippov",
"Xavier Bernard",
"Yana Chernobilsky",
"Yaw Etse",
"YinYangBalance.Asia",
"Yu Jun",
"Zach Cardwell",
],
}
class ClacksSolution2Thumbnail(Scene):
def construct(self):
self.add_scene1()
self.add_scene2()
arrow = TexMobject("\\Updownarrow")
arrow.set_height(2)
arrow.set_color(YELLOW)
arrow.set_stroke(Color("red"), 2, background=True)
self.add(arrow)
def add_scene1(self):
scene1 = Thumbnail(
sliding_blocks_config={
"block1_config": {
"label_text": "$100^{d}$ kg",
"distance": 8,
},
}
)
VGroup(*scene1.mobjects).shift(0.9 * DOWN)
scene1.remove(scene1.question)
self.add(*scene1.mobjects)
rect = FullScreenFadeRectangle(fill_opacity=1)
rect.shift(FRAME_HEIGHT * UP / 2)
self.add(rect)
def add_scene2(self):
scene2 = ReflectWorldThroughMirrorNew(
skip_animations=True,
file_writer_config={
"write_to_movie": False,
},
end_at_animation_number=18,
center=1.5 * UP,
)
worlds = VGroup(scene2.world, *scene2.reflected_worlds)
mirrors = VGroup(*[rw[1] for rw in worlds])
mirrors.set_stroke(width=5)
randys = VGroup(*[rw[-1] for rw in worlds])
triangles = VGroup(*[rw[0] for rw in worlds])
trajectories = VGroup(
scene2.trajectory,
*scene2.reflected_trajectories
)
trajectories.set_stroke(YELLOW, 1)
beams1, beams2 = [
scene2.get_shooting_beam_anims(
path,
max_stroke_width=20,
max_time_width=1,
num_flashes=50,
)
for path in [
scene2.trajectory,
scene2.ghost_trajectory,
]
]
beams = beams1 + beams2
beams = beams2
flashes = VGroup()
for beam in beams:
beam.update(0.5)
flashes.add(beam.mobject)
dot = Dot(color=YELLOW, radius=0.1)
dot.move_to(flashes[0].get_left())
flashes.add(dot)
self.add(mirrors, triangles, randys)
self.add(trajectories[0].set_stroke(width=3))
self.add(flashes)

View file

@ -1,4 +1,5 @@
from big_ol_pile_of_manim_imports import *
from old_projects.clacks.solution2.position_phase_space import ShowMomentumConservation
class ConnectionToOptics(Scene):
@ -145,7 +146,7 @@ class ConnectionToOptics(Scene):
title = VGroup(*map(TextMobject, [
"Angle of\\\\Incidence",
"=",
"Angle of\\\\Refraction",
"Angle of\\\\Reflection",
])).arrange_submobjects(RIGHT)
title.set_color(YELLOW)
h_line = Line(LEFT, RIGHT)
@ -203,3 +204,115 @@ class ConnectionToOptics(Scene):
class ConnectionToOpticsTransparent(ConnectionToOptics):
pass
class RearrangeMomentumEquation(ShowMomentumConservation):
def setup(self):
pass # Don't build all the things
def construct(self):
self.add(FullScreenFadeRectangle(
fill_color=BLACK,
fill_opacity=0.95,
))
self.show_initial_dot_product()
self.show_with_x_and_y()
def show_initial_dot_product(self):
equation = self.get_momentum_equation()
dot_product = self.get_dot_product(
"m_1", "m_2", "v_1", "v_2"
)
dot_product.next_to(equation, DOWN, LARGE_BUFF)
m_array, dot, v_array, rhs = dot_product
m_array.get_entries().set_color(BLUE)
v_array.get_entries().set_color(RED)
self.add(equation)
self.play(FadeInFromDown(VGroup(
m_array.get_brackets(), dot,
v_array.get_brackets(), rhs,
)))
self.play(TransformFromCopy(
equation.get_parts_by_tex("m_"),
m_array.get_entries(),
))
self.play(TransformFromCopy(
equation.get_parts_by_tex("v_"),
v_array.get_entries(),
))
self.wait()
self.simple_dot_product = dot_product
self.momentum_equation = equation
def show_with_x_and_y(self):
simple_dot_product = self.simple_dot_product
momentum_equation = self.momentum_equation
new_equation = TexMobject(
"\\sqrt{m_1}",
"\\left(", "\\sqrt{m_1}", "v_1", "\\right)",
"+", "\\sqrt{m_2}",
"\\left(", "\\sqrt{m_2}", "v_2", "\\right)",
"=", "\\text{const.}",
)
new_equation.set_color_by_tex_to_color_map({
"m_": BLUE,
"v_": RED,
})
new_equation.next_to(momentum_equation, DOWN, MED_LARGE_BUFF)
x_term = new_equation[1:5]
y_term = new_equation[7:11]
x_brace = Brace(x_term, DOWN)
y_brace = Brace(y_term, DOWN)
dx_dt = x_brace.get_tex("dx / dt")
dy_dt = y_brace.get_tex("dy / dt")
new_eq_group = VGroup(
new_equation, x_brace, y_brace, dx_dt, dy_dt
)
new_eq_group.generate_target()
new_dot_product = self.get_dot_product()
m_array, dot, d_array, rhs = new_dot_product
new_dot_product.next_to(momentum_equation, DOWN)
new_eq_group.target.next_to(new_dot_product, DOWN, LARGE_BUFF)
self.play(
FadeInFrom(new_equation, UP),
simple_dot_product.to_edge, DOWN, LARGE_BUFF,
)
self.wait()
self.play(
GrowFromCenter(x_brace),
GrowFromCenter(y_brace),
FadeInFrom(dx_dt, UP),
FadeInFrom(dy_dt, UP),
)
self.wait()
self.play(
FadeIn(VGroup(
m_array.get_brackets(), dot,
d_array.get_brackets(), rhs
)),
MoveToTarget(new_eq_group)
)
self.play(TransformFromCopy(
VGroup(
VGroup(new_equation[0]),
VGroup(new_equation[6]),
),
m_array.get_entries(),
))
self.play(TransformFromCopy(
VGroup(dx_dt, dy_dt),
d_array.get_entries(),
))
self.wait()
class NewSceneName(Scene):
def construct(self):
pass

View file

@ -5,14 +5,17 @@ import sys
import importlib
from manimlib.constants import PRODUCTION_QUALITY_CAMERA_CONFIG
from manimlib.constants import PRODUCTION_QUALITY_FRAME_DURATION
from manimlib.constants import VIDEO_DIR
from manimlib.config import get_module
from manimlib.extract_scene import is_child_scene
from manimlib.utils.file_ops import get_movie_output_directory
def get_sorted_scene_classes(module_name):
module = get_module(module_name)
if hasattr(module, "ALL_SCENE_CLASSES"):
return module.ALL_SCENE_CLASSES
# Otherwise, deduce from the order in which
# they're defined in a file
importlib.import_module(module.__name__)
line_to_scene = {}
name_scene_list = inspect.getmembers(
@ -30,17 +33,19 @@ def get_sorted_scene_classes(module_name):
]
def stage_animations(module_name):
def stage_scenes(module_name):
scene_classes = get_sorted_scene_classes(module_name)
if len(scene_classes) == 0:
print("There are no rendered animations from this module")
return
output_directory_kwargs = {
"camera_config": PRODUCTION_QUALITY_CAMERA_CONFIG,
}
animation_dir = get_movie_output_directory(
scene_classes[0], **output_directory_kwargs
# output_directory_kwargs = {
# "camera_config": PRODUCTION_QUALITY_CAMERA_CONFIG,
# }
# TODO, fix this
animation_dir = os.path.join(
VIDEO_DIR, "clacks_solution2", "1440p60"
)
#
files = os.listdir(animation_dir)
sorted_files = []
for scene_class in scene_classes:
@ -92,4 +97,4 @@ if __name__ == "__main__":
if len(sys.argv) < 2:
raise Exception("No module given.")
module_name = sys.argv[1]
stage_animations(module_name)
stage_scenes(module_name)