From 9c9104dcfab4d63c219d4fcffab5f3476a8d336e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:37:36 -0800 Subject: [PATCH 01/15] Fixed GrowFromPoint --- manimlib/animation/creation.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/manimlib/animation/creation.py b/manimlib/animation/creation.py index 42f4f92f..dd058c05 100644 --- a/manimlib/animation/creation.py +++ b/manimlib/animation/creation.py @@ -246,11 +246,10 @@ class GrowFromPoint(Transform): def __init__(self, mobject, point, **kwargs): digest_config(self, kwargs) target = mobject.copy() - point_mob = VectorizedPoint(point) + mobject.scale(0) + mobject.move_to(point) if self.point_color: - point_mob.set_color(self.point_color) - mobject.replace(point_mob) - mobject.set_color(point_mob.get_color()) + mobject.set_color(self.point_color) Transform.__init__(self, mobject, target, **kwargs) From a62eb989de926684759b52ba17251cafb1733957 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:38:25 -0800 Subject: [PATCH 02/15] Simpler animation config for ShowCreationThenFadeAround --- manimlib/animation/indication.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/manimlib/animation/indication.py b/manimlib/animation/indication.py index 6b870378..f4f52bb6 100644 --- a/manimlib/animation/indication.py +++ b/manimlib/animation/indication.py @@ -191,10 +191,7 @@ class ShowCreationThenDestructionAround(AnimationOnSurroundingRectangle): class ShowCreationThenFadeAround(AnimationOnSurroundingRectangle): CONFIG = { - "rect_to_animation": lambda rect: Succession( - ShowCreation, rect, - FadeOut, rect, - ) + "rect_to_animation": ShowCreationThenFadeOut } From 10c95a12dcbbd64fd59f39297ddb94698b0f7305 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:39:27 -0800 Subject: [PATCH 03/15] --output_file_name config shouldn't have nargs --- manimlib/config.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manimlib/config.py b/manimlib/config.py index 19063713..96fbbe47 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -74,7 +74,6 @@ def parse_cli(): ), parser.add_argument( "-o", "--output_file_name", - nargs=1, help="Specify the name of the output file, if" "it should be different from the scene class name", ) From acc26fa8e43b345fe1c9eeb354f7e4fe13762dcb Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:40:06 -0800 Subject: [PATCH 04/15] Comment to complain about GraphScene and remind myself to re-factor or replace it --- manimlib/scene/graph_scene.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/manimlib/scene/graph_scene.py b/manimlib/scene/graph_scene.py index 0f23ad8b..868e2858 100644 --- a/manimlib/scene/graph_scene.py +++ b/manimlib/scene/graph_scene.py @@ -20,7 +20,9 @@ from manimlib.utils.color import invert_color from manimlib.utils.space_ops import angle_of_vector # TODO, this should probably reimplemented entirely, especially so as to -# better reuse code from mobject/coordinate_systems +# better reuse code from mobject/coordinate_systems. +# Also, I really dislike how the configuration is set up, this +# is way too messy to work with. class GraphScene(Scene): From c9db4e741c115e6472167497941344efc928cf72 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:40:40 -0800 Subject: [PATCH 05/15] Slight changes to ShowTwoPopulations --- old_projects/div_curl.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/old_projects/div_curl.py b/old_projects/div_curl.py index e6e55675..5a43966c 100644 --- a/old_projects/div_curl.py +++ b/old_projects/div_curl.py @@ -2998,6 +2998,7 @@ class ShowTwoPopulations(Scene): "start_num_rabbits": 20, "animal_height": 0.5, "final_wait_time": 30, + "count_word_scale_val": 1, } def construct(self): @@ -3093,9 +3094,13 @@ class ShowTwoPopulations(Scene): # Add counts for foxes and rabbits labels = self.get_pop_labels() num_foxes = Integer(10) + num_foxes.scale(self.count_word_scale_val) num_foxes.next_to(labels[0], RIGHT) + num_foxes.align_to(labels[0][1], DOWN) num_rabbits = Integer(10) + num_rabbits.scale(self.count_word_scale_val) num_rabbits.next_to(labels[1], RIGHT) + num_rabbits.align_to(labels[1][1], DOWN) self.add(ContinualChangingDecimal( num_foxes, lambda a: get_num_foxes() @@ -3156,6 +3161,8 @@ class ShowTwoPopulations(Scene): TextMobject("\\# Foxes: "), TextMobject("\\# Rabbits: "), ) + for label in labels: + label.scale(self.count_word_scale_val) labels.arrange_submobjects(RIGHT, buff=2) labels.to_edge(UP) return labels From 118837cac918bb9c977edaeb20192f1ba015768e Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:41:21 -0800 Subject: [PATCH 06/15] Actually, stage scenes should deal in partial movie files by default --- stage_scenes.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/stage_scenes.py b/stage_scenes.py index 53b099d7..6d595ad5 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -48,26 +48,29 @@ def stage_animations(module_name): sorted_files = [] for scene_class in scene_classes: scene_name = scene_class.__name__ + clips = [f for f in files if f.startswith(scene_name + ".")] + for clip in clips: + sorted_files.append(os.path.join(animation_dir, clip)) # Partial movie file directory - pmf_dir = get_partial_movie_output_directory( - scene_class, **output_directory_kwargs - ) - if os.path.exists(pmf_dir): - for extension in [".mov", ".mp4"]: - int_files = get_sorted_integer_files( - pmf_dir, extension=extension - ) - for file in int_files: - sorted_files.append(os.path.join(pmf_dir, file)) - else: - for clip in [f for f in files if f.startswith(scene_name + ".")]: - sorted_files.append(os.path.join(animation_dir, clip)) + # movie_dir = get_movie_output_directory( + # scene_class, **output_directory_kwargs + # ) + # if os.path.exists(movie_dir): + # for extension in [".mov", ".mp4"]: + # int_files = get_sorted_integer_files( + # pmf_dir, extension=extension + # ) + # for file in int_files: + # sorted_files.append(os.path.join(pmf_dir, file)) + # else: - animation_subdir = os.path.dirname(animation_dir) + # animation_subdir = os.path.dirname(animation_dir) count = 0 while True: staged_scenes_dir = os.path.join( - animation_subdir, "staged_scenes_{}".format(count) + animation_dir, + os.pardir, + "staged_scenes_{}".format(count) ) if not os.path.exists(staged_scenes_dir): os.makedirs(staged_scenes_dir) @@ -82,8 +85,7 @@ def stage_animations(module_name): symlink_name = os.path.join( staged_scenes_dir, "Scene_{:03}_{}".format( - count, - "".join(f.split(os.sep)[-2:]) + count, f.split(os.sep)[-1] ) ) os.symlink(f, symlink_name) From 4359705b9762658e90a209b1bb9500686946d0be Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Tue, 22 Jan 2019 15:41:44 -0800 Subject: [PATCH 07/15] Final animations for first clacks solution video --- active_projects/clacks.py | 28 +- active_projects/clacks_solution1.py | 867 ++++++++++++++++++++++++---- 2 files changed, 788 insertions(+), 107 deletions(-) diff --git a/active_projects/clacks.py b/active_projects/clacks.py index ec80ed76..30842d06 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks.py @@ -848,9 +848,14 @@ class BlocksAndWallExampleMass1e10(BlocksAndWallExample): class DigitsOfPi(Scene): + CONFIG = {"n_digits": 9} + def construct(self): + nd = self.n_digits + pow10 = int(10**nd) + rounded_pi = int(pow10 * PI) / pow10 equation = TexMobject( - "\\pi = 3.14159265..." + ("\\pi = {:." + str(nd) + "f}...").format(rounded_pi) ) equation.set_color(YELLOW) pi_creature = Randolph(color=YELLOW) @@ -858,9 +863,11 @@ class DigitsOfPi(Scene): pi_creature.scale(1.4) pi_creature.move_to(equation[0], DOWN) self.add(pi_creature, equation[1]) - for digit in equation[2:]: - self.add(digit) - self.wait(0.1) + self.play(ShowIncreasingSubsets( + equation[2:], + rate_func=None, + run_time=1, + )) self.play(Blink(pi_creature)) self.wait() @@ -1569,15 +1576,25 @@ class Thumbnail(BlocksAndWallExample): } def construct(self): + self.thicken_lines() + self.grow_labels() + self.add_vector() + self.add_text() + + def thicken_lines(self): self.floor.set_stroke(WHITE, 10) self.wall.set_stroke(WHITE, 10) self.wall[1:].set_stroke(WHITE, 4) + + def grow_labels(self): blocks = self.blocks for block in blocks.block1, blocks.block2: block.remove(block.label) block.label.scale(2.5, about_point=block.get_top()) self.add(block.label) + def add_vector(self): + blocks = self.blocks arrow = Vector( 2.5 * LEFT, color=RED, @@ -1590,9 +1607,10 @@ class Thumbnail(BlocksAndWallExample): ) self.add(arrow) + def add_text(self): question = TextMobject("How many\\\\collisions?") question.scale(2.5) question.to_edge(UP) question.set_color(YELLOW) question.set_stroke(RED, 2, background=True) - self.add(question) \ No newline at end of file + self.add(question) diff --git a/active_projects/clacks_solution1.py b/active_projects/clacks_solution1.py index 83088a20..d67fda47 100644 --- a/active_projects/clacks_solution1.py +++ b/active_projects/clacks_solution1.py @@ -1,8 +1,8 @@ from big_ol_pile_of_manim_imports import * from active_projects.clacks import * +from old_projects.div_curl import ShowTwoPopulations -# TODO, add solution image class FromPuzzleToSolution(MovingCameraScene): def construct(self): big_rect = FullScreenFadeRectangle() @@ -20,7 +20,7 @@ class FromPuzzleToSolution(MovingCameraScene): images = Group( ImageMobject("BlocksAndWallExampleMass16"), - ImageMobject("SphereSurfaceProof2"), # TODO + ImageMobject("AnalyzeCircleGeometry"), ) for title, rect, image in zip(titles, rects, images): title.scale(1.5) @@ -138,6 +138,42 @@ class BlocksAndWallExampleMassTrillion(BlocksAndWallExample): } +class First6DigitsOfPi(DigitsOfPi): + CONFIG = {"n_digits": 6} + + +class FavoritesInDescription(Scene): + def construct(self): + words = TextMobject("(See the description for \\\\ some favorites)") + words.scale(1.5) + self.add(words) + + +class V1EqualsV2Line(Scene): + def construct(self): + line = Line(LEFT, 7 * RIGHT) + eq = TexMobject("v_1", "=", "v_2") + eq.set_color_by_tex("v_", RED) + eq.next_to(RIGHT, UR, SMALL_BUFF) + self.play( + Write(eq, run_time=1), + ShowCreation(line), + ) + self.wait() + + +class PhaseSpaceTitle(Scene): + def construct(self): + title = TextMobject("Phase space") + title.scale(1.5) + title.to_edge(UP) + rect = ScreenRectangle(height=6) + rect.next_to(title, DOWN) + self.add(rect) + self.play(Write(title, run_time=1)) + self.wait() + + class AskAboutFindingNewVelocities(Scene): CONFIG = { "floor_y": -3, @@ -267,6 +303,9 @@ class AskAboutFindingNewVelocities(Scene): self.halt() self.play(randy.look_at, velocity_labels[-1]) self.play(Blink(randy)) + self.play(randy.change, "confused") + self.play(Blink(randy)) + self.wait() self.play( FadeInFrom(energy_words, RIGHT), FadeInFromDown(energy_expression), @@ -409,7 +448,8 @@ class AskAboutFindingNewVelocities(Scene): v_decimals.add_updater(update_v_decimals) self.add(v_decimals) self.unhalt() - for x in range(4): + self.vps_point.save_state() + for x in range(8): self.go_through_next_collision() energy_decimal.clear_updaters() momentum_decimal.set_value(get_momentum()) @@ -427,13 +467,14 @@ class AskAboutFindingNewVelocities(Scene): ], ]) self.unhalt() + self.vps_point.restore() momentum_decimal.add_updater( lambda m: m.set_value(get_momentum()) ) momentum_decimal.add_updater( lambda m: m.next_to(momentum_const_brace, UP, SMALL_BUFF) ) - for x in range(4): + for x in range(9): self.go_through_next_collision() self.wait(10) @@ -596,6 +637,7 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): "unit_size": 0.7, }, }, + "momentum_line_scale_factor": 4, } def construct(self): @@ -747,9 +789,10 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): ellipse = self.ellipse = Circle(color=YELLOW) ellipse.set_stroke(BLACK, 5, background=True) ellipse.rotate(PI) + mass_ratio = self.block1.mass / self.block2.mass ellipse.replace( Polygon(*[ - axes.coords_to_point(x, y * np.sqrt(10)) + axes.coords_to_point(x, y * np.sqrt(mass_ratio)) for x, y in [(1, 0), (0, 1), (-1, 0), (0, -1)] ]), stretch=True @@ -776,8 +819,9 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): lambda m: m.move_to(ellipse.get_left()) ) + mass_ratio = self.block1.mass / self.block2.mass brief_circle = ellipse.copy() - brief_circle.stretch(np.sqrt(10), 0) + brief_circle.stretch(np.sqrt(mass_ratio), 0) brief_circle.set_stroke(WHITE, 2) xy_equation = self.xy_equation = TexMobject( @@ -892,7 +936,7 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): m1 = self.block1.mass m2 = self.block2.mass line = Line(np.sqrt(m2) * LEFT, np.sqrt(m1) * DOWN) - line.scale(4) + line.scale(self.momentum_line_scale_factor) line.set_stroke(GREEN, 3) line.move_to(vps_dot) @@ -989,6 +1033,20 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): ) self.wait() + group = VGroup( + self.ellipse, + self.lines[-1], + self.vps_dot.copy().clear_updaters() + ) + for x in range(2): + self.play( + Rotate( + group, PI, RIGHT, + about_point=self.axes.coords_to_point(0, 0) + ), + ) + self.remove(group[-1]) + def show_remaining_collisions(self): line = self.momentum_line # slope_group = self.slope_group @@ -1019,6 +1077,8 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): # Helpers def add_update_line(self, func): + if not hasattr(self, "lines"): + self.lines = VGroup() if hasattr(self, "vps_dot"): old_vps_point = self.vps_dot.get_center() func() @@ -1027,6 +1087,7 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): line = Line(old_vps_point, new_vps_point) line.set_stroke(WHITE, 2) self.add(line) + self.lines.add(line) else: func() @@ -1037,17 +1098,98 @@ class IntroduceVelocityPhaseSpace(AskAboutFindingNewVelocities): self.add_update_line(super().reflect_block2) +class IntroduceVelocityPhaseSpaceWith16(IntroduceVelocityPhaseSpace): + CONFIG = { + "block1_config": { + "mass": 16, + "velocity": -0.5, + }, + "momentum_line_scale_factor": 0, + } + + +class SimpleRect(Scene): + def construct(self): + self.add(Rectangle(width=6, height=2, color=WHITE)) + + +class SurprisedRandy(Scene): + def construct(self): + randy = Randolph() + self.play(FadeIn(randy)) + self.play(randy.change, "surprised", 3 * UR) + self.play(Blink(randy)) + self.wait() + self.play(randy.change, "pondering", 3 * UR) + self.play(Blink(randy)) + self.wait(2) + self.play(FadeOut(randy)) + + +class HuntForPi(TeacherStudentsScene): + def construct(self): + self.student_says( + "Hunt for $\\pi$!", + bubble_kwargs={"direction": LEFT}, + target_mode="hooray" + ) + self.change_all_student_modes( + "hooray", + added_anims=[self.teacher.change, "happy"] + ) + self.wait() + + +class StretchBySqrt10(Scene): + def construct(self): + arrow = DoubleArrow(2 * LEFT, 2 * RIGHT) + arrow.tip[1].shift(0.05 * LEFT) + value = TexMobject("\\sqrt{10}") + value.next_to(arrow, UP) + arrow.save_state() + arrow.stretch(0, 0) + self.play( + Restore(arrow), + Write(value, run_time=1), + ) + self.wait() + + +class XCoordNegative(Scene): + def construct(self): + rect = Rectangle(height=4, width=4) + rect.set_stroke(width=0) + rect.set_fill(RED, 0.5) + rect.save_state() + rect.stretch(0, 0, about_edge=RIGHT) + self.play(Restore(rect)) + self.wait() + + +class YCoordZero(Scene): + def construct(self): + rect = Rectangle(height=4, width=8) + rect.set_stroke(width=0) + rect.set_fill(WHITE, 0.5) + rect.save_state() + self.play( + rect.stretch, 0.01, 1, + rect.set_fill, {"opacity": 1} + ) + self.wait() + + class CircleDiagramFromSlidingBlocks(Scene): CONFIG = { "BlocksAndWallSceneClass": BlocksAndWallExampleMass1e1, "circle_config": { "radius": 2, "stroke_color": YELLOW, - "stroke_width": 2, + "stroke_width": 3, }, "lines_style": { "stroke_color": WHITE, - "stroke_width": 1, + "stroke_width": 2, }, "axes_config": { "style": { @@ -1097,6 +1239,7 @@ class CircleDiagramFromSlidingBlocks(Scene): last_time = time dot.move_to(line.get_end()) self.add(line, dot) + self.wait() def get_circle(self): circle = Circle(**self.circle_config) @@ -1348,8 +1491,13 @@ class AnalyzeCircleGeometry(CircleDiagramFromSlidingBlocks, MovingCameraScene): for n, arc in enumerate(all_arcs): count_mob = Integer(n + 1) count_mob.scale(0.75) + buff = SMALL_BUFF + if len(all_arcs) > 100: + count_mob.scale(0.1) + count_mob.set_stroke(WHITE, 0.25) + buff = 0.4 * SMALL_BUFF point = arc.point_from_proportion(0.5) - count_mob.next_to(point, normalize(point), SMALL_BUFF) + count_mob.next_to(point, normalize(point), buff) arc_counts.add(count_mob) self.play( @@ -1616,9 +1764,9 @@ class InscribedAngleTheorem(Scene): self.add(circle, center_dot) angle_trackers = self.angle_trackers = VGroup( - ValueTracker(TAU / 8), + ValueTracker(TAU / 4), ValueTracker(PI), - ValueTracker(-TAU / 8), + ValueTracker(-TAU / 4), ) def get_point(angle): @@ -1719,11 +1867,6 @@ class InscribedAngleTheorem(Scene): Write(theta_label), ) self.wait() - self.play( - TransformFromCopy(lines, center_lines), - TransformFromCopy(theta_label, two_theta_label), - ) - self.wait() # Add updaters labels.add_updater(update_labels) @@ -1733,10 +1876,24 @@ class InscribedAngleTheorem(Scene): theta_label.add_updater(lambda m: m.become(get_theta_label())) two_theta_label.add_updater(lambda m: m.become(get_2theta_label())) - self.add( - labels, lines, center_lines, dots, - theta_label, two_theta_label, + self.add(labels, lines, dots, theta_label) + # Further animations + self.play( + angle_trackers[0].set_value, TAU / 8, ) + self.play( + angle_trackers[2].set_value, -TAU / 8, + ) + self.wait() + center_lines.update() + two_theta_label.update() + self.play( + TransformFromCopy(lines.copy().clear_updaters(), center_lines), + TransformFromCopy(theta_label.copy().clear_updaters(), two_theta_label), + ) + self.wait() + + self.add(center_lines, two_theta_label) def let_point_vary(self): p1_tracker, p2_tracker, p3_tracker = self.angle_trackers @@ -1765,6 +1922,22 @@ class InscribedAngleTheorem(Scene): self.wait() +class SimpleSlopeLabel(Scene): + def construct(self): + label = TexMobject( + "\\text{Slope}", "=", + "-\\frac{\\sqrt{m_1}}{\\sqrt{m_2}}" + ) + vector = Vector(DOWN + 2 * LEFT, color=WHITE) + vector.move_to(label[0].get_bottom(), UR) + vector.shift(SMALL_BUFF * DOWN) + self.play( + Write(label), + GrowArrow(vector), + ) + self.wait() + + class AddTwoThetaManyTimes(Scene): def construct(self): expression = TexMobject( @@ -1822,35 +1995,17 @@ class AddTwoThetaManyTimes(Scene): return result def show_example(self): + equation = self.get_changable_equation(0.01, n_decimal_places=2) expression, brace, question = self.central_question_group - N_mob = Integer(1) - N_mob.match_height(expression[0]) - N_mob.set_color(BLUE) - dot_theta_eq = TexMobject("\\cdot", "(0.01)", "=") - rhs = DecimalNumber(0, num_decimal_places=2) - rhs.set_color(RED) - dot_theta_eq.move_to(expression[1:4]) - dot_theta_eq.shift(2 * DOWN) + N_mob, dot_theta_eq, rhs, comp_pi = equation - def align_value(mob): - mob.align_to(dot_theta_eq[1][1:-1], DOWN) - - N_mob.add_updater( - lambda m: m.next_to(dot_theta_eq, LEFT, SMALL_BUFF) - ) - N_mob.add_updater(align_value) - rhs.add_updater( - lambda m: m.set_value(0.01 * N_mob.get_value()) - ) - rhs.add_updater( - lambda m: m.next_to(dot_theta_eq, RIGHT, 2 * SMALL_BUFF) - ) - rhs.add_updater(align_value) + equation.next_to(expression, DOWN, 2, aligned_edge=LEFT) self.play( TransformFromCopy(expression[0], N_mob), TransformFromCopy(expression[1:4], dot_theta_eq), TransformFromCopy(expression[4], rhs), + TransformFromCopy(expression[4], comp_pi), ) self.wait() self.play( @@ -1863,6 +2018,49 @@ class AddTwoThetaManyTimes(Scene): self.wait() self.play(ShowCreationThenFadeAround(N_mob)) + # + def get_changable_equation(self, value, tex_string=None, n_decimal_places=10): + int_mob = Integer(1) + int_mob.set_color(BLUE) + formatter = "({:0." + str(n_decimal_places) + "f})" + tex_string = tex_string or formatter.format(value) + tex_mob = TexMobject("\\cdot", tex_string, "=") + rhs = DecimalNumber(value, num_decimal_places=n_decimal_places) + + def align_number(mob): + y0 = mob[0].get_center()[1] + y1 = tex_mob[1][1:-1].get_center()[1] + mob.shift((y1 - y0) * UP) + + int_mob.add_updater( + lambda m: m.next_to(tex_mob, LEFT, SMALL_BUFF) + ) + int_mob.add_updater(align_number) + rhs.add_updater( + lambda m: m.set_value(value * int_mob.get_value()) + ) + rhs.add_updater( + lambda m: m.next_to(tex_mob, RIGHT, SMALL_BUFF) + ) + rhs.add_updater(align_number) + + def get_comp_pi(): + if rhs.get_value() < np.pi: + result = TexMobject("< \\pi") + result.set_color(GREEN) + elif rhs.get_value() > np.pi: + result = TexMobject("> \\pi") + result.set_color(RED) + else: + result = TexMobject("= \\pi") + result.next_to(rhs, RIGHT, 2 * SMALL_BUFF) + result[1].scale(1.5, about_edge=LEFT) + return result + + comp_pi = updating_mobject_from_func(get_comp_pi) + + return VGroup(int_mob, tex_mob, rhs, comp_pi) + class AskAboutTheta(TeacherStudentsScene): def construct(self): @@ -2029,11 +2227,13 @@ class ComputeThetaFor1e4(AnalyzeCircleGeometry): sm = movers.submobjects sm[-1], sm[-2] = sm[-2], sm[-1] self.play(LaggedStart( - Transform, movers, + Transform, movers[:-1], lambda m: (m, m.target), - lag_ratio=0.3, - run_time=2, + lag_ratio=1, + run_time=1, + path_arc=PI / 6, )) + self.play(MoveToTarget(movers[-1])) self.remove(movers) self.add(arctan_equation) self.play(ShowCreationThenFadeAround(arctan_equation)) @@ -2056,7 +2256,8 @@ class ThetaChart(Scene): ]) titles.scale(1.5) titles.arrange_submobjects(RIGHT, buff=1.5) - titles[-1].shift(MED_SMALL_BUFF * RIGHT) + titles[1].shift(MED_SMALL_BUFF * LEFT) + titles[2].shift(MED_SMALL_BUFF * RIGHT) titles.to_corner(UL) lines = VGroup() @@ -2087,14 +2288,14 @@ class ThetaChart(Scene): entries = [ ( "$m_1$ : $m_2$", - "$\\arctan(\\sqrt{m2} / \\sqrt{m1})$", + "$\\arctan(\\sqrt{m_2} / \\sqrt{m_1})$", "" ) ] + [ ( "{:,} : 1".format(10**(2 * exp)), "$\\arctan(1 / {:,})$".format(10**exp), - "{:0.10f}".format(np.arctan(10**(-exp))) + self.get_theta_decimal(exp), ) for exp in [1, 2, 3, 4, 5] ] @@ -2119,9 +2320,11 @@ class ThetaChart(Scene): self.play( LaggedStart( FadeInFromDown, - VGroup(*[em[:2] for em in entry_mobs]) + VGroup(*[em[:2] for em in entry_mobs]), ), - LaggedStart(ShowCreation, h_lines[1:]) + LaggedStart(ShowCreation, h_lines[1:]), + lag_ratio=0.1, + run_time=5, ) self.entry_mobs = entry_mobs @@ -2148,6 +2351,7 @@ class ThetaChart(Scene): rhs.to_edge(RIGHT, buff=MED_SMALL_BUFF) value.generate_target() value.target.set_fill(opacity=1) + value.target.scale(0.9) value.target.next_to(rhs, LEFT, SMALL_BUFF) self.play( @@ -2163,6 +2367,11 @@ class ThetaChart(Scene): self.wait() value.add(rhs) + def get_theta_decimal(self, exp): + theta = np.arctan(10**(-exp)) + rounded_theta = np.floor(1e10 * theta) / 1e10 + return "{:0.10f}\\dots".format(rounded_theta) + class CentralQuestionFor1e2(AddTwoThetaManyTimes): CONFIG = { @@ -2211,7 +2420,7 @@ class CentralQuestionFor1e2(AddTwoThetaManyTimes): self.wait() self.play(*[ - ChangeDecimalToValue(int_mob, max_count, run_time=3) + ChangeDecimalToValue(int_mob, max_count, run_time=8) for int_mob in int_mobs ]) self.wait() @@ -2227,49 +2436,6 @@ class CentralQuestionFor1e2(AddTwoThetaManyTimes): self.play(ShowCreationThenFadeAround(int_mobs[1])) self.wait() - # - def get_changable_equation(self, value, tex_string=None, n_decimal_places=10): - int_mob = Integer(1) - int_mob.set_color(BLUE) - formatter = "({:0." + str(n_decimal_places) + "f})" - tex_string = tex_string or formatter.format(value) - tex_mob = TexMobject("\\cdot", tex_string, "=") - rhs = DecimalNumber(value, num_decimal_places=n_decimal_places) - - def align_number(mob): - y0 = mob[0].get_center()[1] - y1 = tex_mob[1][1:-1].get_center()[1] - mob.shift((y1 - y0) * UP) - - int_mob.add_updater( - lambda m: m.next_to(tex_mob, LEFT, SMALL_BUFF) - ) - int_mob.add_updater(align_number) - rhs.add_updater( - lambda m: m.set_value(value * int_mob.get_value()) - ) - rhs.add_updater( - lambda m: m.next_to(tex_mob, RIGHT, SMALL_BUFF) - ) - rhs.add_updater(align_number) - - def get_comp_pi(): - if rhs.get_value() < np.pi: - result = TexMobject("< \\pi") - result.set_color(GREEN) - elif rhs.get_value() > np.pi: - result = TexMobject("> \\pi") - result.set_color(RED) - else: - result = TexMobject("= \\pi") - result.next_to(rhs, RIGHT, 2 * SMALL_BUFF) - result[1].scale(1.5, about_edge=LEFT) - return result - - comp_pi = updating_mobject_from_func(get_comp_pi) - - return VGroup(int_mob, tex_mob, rhs, comp_pi) - class AnalyzeCircleGeometry1e2(AnalyzeCircleGeometry): CONFIG = { @@ -2303,7 +2469,7 @@ class AskAboutArctanOfSmallValues(TeacherStudentsScene): "erm", "sassy", "confused" ) ) - self.wait() + self.look_at(3 * UL) self.play(equation1.shift, UP) self.play( TransformFromCopy( @@ -2317,20 +2483,110 @@ class AskAboutArctanOfSmallValues(TeacherStudentsScene): VGroup(*[equation2[i] for i in (2, 3, 5)]), ), self.get_student_changes( - "confused", "erm", "sassy" + "confused", "erm", "sassy", ), ) - self.wait() - self.student_says("Why?", target_mode="maybe") + self.look_at(3 * UL) self.wait(3) + # self.student_says("Why?", target_mode="maybe") + # self.wait(3) def add_title(self): title = TextMobject("For small $x$") subtitle = TextMobject("(e.g. $x = 0.001$)") - title.scale(1.5) - title.to_edge(UP, buff=MED_SMALL_BUFF) + subtitle.scale(0.75) subtitle.next_to(title, DOWN) - self.add(title, subtitle) + title.add(subtitle) + # title.scale(1.5) + # title.to_edge(UP, buff=MED_SMALL_BUFF) + title.move_to(self.hold_up_spot) + title.to_edge(UP) + self.add(title) + + +class ActanAndTanGraphs(GraphScene): + CONFIG = { + "x_min": -PI / 8, + "x_max": 5 * PI / 8, + "y_min": -PI / 8, + "y_max": 4 * PI / 8, + "x_tick_frequency": PI / 8, + "x_leftmost_tick": -PI / 8, + "y_tick_frequency": PI / 8, + "y_leftmost_tick": -PI / 8, + "x_axis_width": 10, + "y_axis_height": 7, + "graph_origin": 2.5 * DOWN + 5 * LEFT, + "num_graph_anchor_points": 500, + } + + def construct(self): + self.setup_axes() + axes = self.axes + labels = VGroup( + TexMobject("\\pi / 8"), + TexMobject("\\pi / 4"), + TexMobject("3\\pi / 8"), + TexMobject("\\pi / 2"), + ) + for n, label in zip(it.count(1), labels): + label.scale(0.75) + label.next_to(self.coords_to_point(n * PI / 8, 0), DOWN) + self.add(label) + + id_graph = self.get_graph(lambda x: x, x_max=1.5) + arctan_graph = self.get_graph(np.arctan, x_max=1.5) + tan_graph = self.get_graph(np.tan, x_max=1.5) + graphs = VGroup(id_graph, arctan_graph, tan_graph) + + id_label = TexMobject("f(x) = x") + arctan_label = TexMobject("\\arctan(x)") + tan_label = TexMobject("\\tan(x)") + labels = VGroup(id_label, arctan_label, tan_label) + for label, graph in zip(labels, graphs): + label.match_color(graph) + label.next_to(graph.points[-1], RIGHT) + if label.get_bottom()[1] > FRAME_HEIGHT / 2: + label.next_to(graph.point_from_proportion(0.75), LEFT) + + arctan_x_tracker = ValueTracker(3 * PI / 8) + arctan_v_line = updating_mobject_from_func( + lambda: self.get_vertical_line_to_graph( + arctan_x_tracker.get_value(), + arctan_graph, + line_class=DashedLine, + color=WHITE, + ) + ) + tan_x_tracker = ValueTracker(2 * PI / 8) + tan_v_line = updating_mobject_from_func( + lambda: self.get_vertical_line_to_graph( + tan_x_tracker.get_value(), + tan_graph, + line_class=DashedLine, + color=WHITE, + ) + ) + + self.add(axes) + self.play( + ShowCreation(id_graph), + Write(id_label) + ) + self.play( + ShowCreation(arctan_graph), + Write(arctan_label) + ) + self.add(arctan_v_line) + self.play(arctan_x_tracker.set_value, 0, run_time=2) + self.wait() + self.play( + TransformFromCopy(arctan_graph, tan_graph), + TransformFromCopy(arctan_label, tan_label), + ) + self.add(tan_v_line) + self.play(tan_x_tracker.set_value, 0, run_time=2) + self.wait() class UnitCircleIntuition(Scene): @@ -2523,6 +2779,413 @@ class UnitCircleIntuition(Scene): self.wait() -class TangentTaylorSeries(Scene): +class TangentTaylorSeries(TeacherStudentsScene): def construct(self): + series = TexMobject( + "\\tan", "(", "\\theta", ")", "=", "\\theta", "+", + "\\frac{1}{3}", "\\theta", "^3", "+", + "\\frac{2}{15}", "\\theta", "^5", "+", "\\cdots", + tex_to_color_map={"\\theta": YELLOW}, + ) + series.move_to(2 * UP) + series.move_to(self.hold_up_spot, DOWN) + series_error = series[7:] + series_error_rect = SurroundingRectangle(series_error) + + example = TexMobject( + "\\tan", "\\left(", "\\frac{1}{100}", "\\right)", + "=", "\\frac{1}{100}", "+", + "\\frac{1}{3}", "\\left(", + "\\frac{1}{1{,}000{,}000}", + "\\right)", "+", + "\\frac{2}{15}", "\\left(", + "\\frac{1}{10{,}000{,}000{,}000}", + "\\right)", "+", "\\cdots", + ) + example.set_color_by_tex("\\frac{1}{1", BLUE) + example.set_width(FRAME_WIDTH - 1) + example.next_to(self.students, UP, buff=2) + example.shift_onto_screen() + error = example[7:] + error_rect = SurroundingRectangle(error) + error_rect.set_color(RED) + error_decimal = DecimalNumber( + np.tan(0.01) - 0.01, + num_decimal_places=15, + ) + error_decimal.next_to(error_rect, DOWN) + approx = TexMobject("\\approx") + approx.next_to(error_decimal, LEFT) + error_decimal.add(approx) + error_decimal.match_color(error_rect) + + self.play( + FadeInFromDown(series), + self.teacher.change, "raise_right_hand", + ) + self.play( + ShowCreation(series_error_rect), + self.get_student_changes(*3 * ["pondering"]) + ) + self.play(FadeOut(series_error_rect)) + self.play( + series.center, series.to_edge, UP, + ) + self.look_at(series) + self.play( + TransformFromCopy(series[:8], example[:8]), + TransformFromCopy(series[8], example[9]), + TransformFromCopy(series[10:12], example[11:13]), + TransformFromCopy(series[12], example[14]), + TransformFromCopy(series[14:], example[16:]), + *map(GrowFromCenter, [example[i] for i in (8, 10, 13, 15)]) + ) + self.change_student_modes("happy", "confused", "sad") + self.play(ShowCreation(error_rect)) + self.play(ShowIncreasingSubsets(error_decimal)) + self.change_all_student_modes("hooray") + self.wait(3) + + +class AnalyzeCircleGeometry1e4(AnalyzeCircleGeometry): + CONFIG = { + "mass_ratio": 10000, + } + + +class SumUpWrapper(Scene): + def construct(self): + title = TextMobject("To sum up:") + title.scale(1.5) + title.to_edge(UP) + screen_rect = ScreenRectangle(height=6) + screen_rect.set_fill(BLACK, 1) + screen_rect.next_to(title, DOWN) + self.add(FullScreenFadeRectangle( + fill_color=DARK_GREY, + fill_opacity=0.5 + )) + self.play( + FadeInFromDown(title), + FadeIn(screen_rect), + ) + self.wait() + + +class ConservationLawSummary(Scene): + def construct(self): + energy_eq = TexMobject( + "\\frac{1}{2}", "m_1", "(", "v_1", ")", "^2", "+", + "\\frac{1}{2}", "m_2", "(", "v_2", ")", "^2", "=", + "\\text{const.}", + ) + energy_word = TextMobject("Energy") + energy_word.scale(2) + circle = Circle(color=YELLOW, radius=2) + energy_group = VGroup(energy_word, energy_eq, circle) + momentum_eq = TexMobject( + "m_1", "v_1", "+", "m_2", "v_2", "=", + "\\text{const.}", + ) + momentum_word = TextMobject("Momentum") + momentum_word.scale(2) + line = Line(ORIGIN, RIGHT + np.sqrt(10) * DOWN) + line.set_color(GREEN) + momentum_group = VGroup(momentum_word, momentum_eq, line) + + equations = VGroup(energy_eq, momentum_eq) + words = VGroup(energy_word, momentum_word) + + for equation in equations: + equation.set_color_by_tex("m_", BLUE) + equation.set_color_by_tex("v_", RED) + + words.arrange_submobjects( + DOWN, buff=3, + ) + words.to_edge(LEFT, buff=1.5) + + for group in energy_group, momentum_group: + arrow = Arrow( + LEFT, 2 * RIGHT, + rectangular_stem_width=0.1, + tip_length=0.5, + color=WHITE + ) + arrow.next_to(group[0], RIGHT) + group[1].next_to(group[0], DOWN) + group[2].next_to(arrow, RIGHT) + group[2].set_stroke(width=6) + group.add(arrow) + # line.scale(4, about_edge=DR) + red_energy_word = energy_word.copy() + red_energy_word.set_fill(opacity=0) + red_energy_word.set_stroke(RED, 2) + + self.add(energy_group, momentum_group) + self.wait() + self.play( + LaggedStart( + ShowCreationThenDestruction, + red_energy_word + ), + ) + for color in [RED, BLUE, PINK, YELLOW]: + self.play(ShowCreation( + circle.copy().set_color(color), + )) + + +class FinalCommentsOnPhaseSpace(Scene): + def construct(self): + self.add_title() + self.show_related_fields() + self.state_to_point() + self.puzzle_as_remnant() + + def add_title(self): + title = self.title = TextMobject("Phase space") + title.scale(2) + title.to_edge(UP) + title.set_color(YELLOW) + + self.play(Write(title)) + + def show_related_fields(self): + title = self.title + + images = Group( + ImageMobject("ClacksThumbnail"), + ImageMobject("PictoralODE"), + # ImageMobject("DoublePendulumStart"), + ImageMobject("MobiusStrip"), + ) + colors = [BLUE_D, GREY_BROWN, BLUE_C] + for image, color in zip(images, colors): + image.set_height(2.5) + image.add(SurroundingRectangle( + image, + color=color, + stroke_width=5, + buff=0, + )) + images.arrange_submobjects(RIGHT) + images.move_to(DOWN) + + arrows = VGroup(*[ + Arrow( + title.get_bottom(), image.get_top(), + color=WHITE, + ) + for image in images + ]) + + for image, arrow in zip(images, arrows): + self.play( + GrowArrow(arrow), + GrowFromPoint(image, title.get_bottom()), + ) + self.wait() + self.wait() + + self.to_fade = Group(images, arrows) + + def state_to_point(self): + state = TextMobject("State") + arrow = Arrow( + 2 * LEFT, 2 * RIGHT, + color=WHITE, + rectangular_stem_width=0.1, + tip_length=0.5 + ) + point = TextMobject("Point") + dynamics = TextMobject("Dynamics") + geometry = TextMobject("Geometry") + words = VGroup(state, point, dynamics, geometry) + for word in words: + word.scale(2) + + group = VGroup(state, arrow, point) + group.arrange_submobjects(RIGHT, buff=MED_LARGE_BUFF) + group.move_to(2.5 * DOWN) + + dynamics.move_to(state, RIGHT) + geometry.move_to(point, LEFT) + + self.play( + FadeOutAndShift(self.to_fade, UP), + FadeInFrom(state, UP) + ) + self.play( + GrowArrow(arrow), + FadeInFrom(point, LEFT) + ) + self.wait(2) + for w1, w2 in [(state, dynamics), (point, geometry)]: + self.play( + FadeOutAndShift(w1, UP), + FadeInFrom(w2, DOWN), + ) + self.wait() + self.wait() + + def puzzle_as_remnant(self): pass + + +class AltShowTwoPopulations(ShowTwoPopulations): + CONFIG = { + "count_word_scale_val": 2, + } + + +class SimpleTeacherHolding(TeacherStudentsScene): + def construct(self): + self.play(self.teacher.change, "raise_right_hand") + self.change_all_student_modes("pondering") + self.wait(3) + + +class EndScreen(PatreonEndScreen): + CONFIG = { + "specific_patrons": [ + "Juan Benet", + "Vassili Philippov", + "Burt Humburg", + "Matt Russell", + "soekul", + "Richard Barthel", + "Nathan Jessurun", + "Ali Yahya", + "dave nicponski", + "Yu Jun", + "Kaustuv DeBiswas", + "Yana Chernobilsky", + "Lukas Biewald", + "Arthur Zey", + "Roy Larson", + "Joseph Kelly", + "Peter Mcinerney", + "Scott Walter, Ph.D.", + "Magnus Lysfjord", + "Evan Phillips", + "Graham", + "Mauricio Collares", + "Quantopian", + "Jordan Scales", + "Lukas -krtek.net- Novy", + "John Shaughnessy", + "Joseph John Cox", + "Ryan Atallah", + "Britt Selvitelle", + "Jonathan Wilson", + "Randy C. Will", + "Magnus Dahlström", + "David Gow", + "J", + "Luc Ritchie", + "Rish Kundalia", + "Bob Sanderson", + "Mathew Bramson", + "Mustafa Mahdi", + "Robert Teed", + "Cooper Jones", + "Jeff Linse", + "John Haley", + "Boris Veselinovich", + "Andrew Busey", + "Awoo", + "Linh Tran", + "Ripta Pasay", + "David Clark", + "Mathias Jansson", + "Clark Gaebel", + "Bernd Sing", + "Jason Hise", + "Ankalagon", + "Dave B", + "Ted Suzman", + "Chris Connett", + "Eric Younge", + "1stViewMaths", + "Jacob Magnuson", + "Jonathan Eppele", + "Delton Ding", + "James Hughes", + "Stevie Metke", + "Yaw Etse", + "John Griffith", + "Magister Mugit", + "Ludwig Schubert", + "Giovanni Filippi", + "Matt Langford", + "Matt Roveto", + "Jameel Syed", + "Richard Burgmann", + "Solara570", + "Alexis Olson", + "Jeff Straathof", + "John V Wertheim", + "Sindre Reino Trosterud", + "Song Gao", + "Peter Ehrnstrom", + "Valeriy Skobelev", + "Art Ianuzzi", + "Michael Faust", + "Omar Zrien", + "Adrian Robinson", + "Federico Lebron", + "Kai-Siang Ang", + "Michael Hardel", + "Nero Li", + "Ryan Williams", + "Charles Southerland", + "Devarsh Desai", + "Hal Hildebrand", + "Jan Pijpers", + "L0j1k", + "Mark B Bahu", + "Márton Vaitkus", + "Richard Comish", + "Zach Cardwell", + "Brian Staroselsky", + "Matthew Cocke", + "Christian Kaiser", + "Danger Dai", + "Dave Kester", + "eaglle", + "Florian Chudigiewitsch", + "Roobie", + "Xavier Bernard", + "YinYangBalance.Asia", + "Eryq Ouithaqueue", + "Kanan Gill", + "j eduardo perez", + "Antonio Juarez", + "Owen Campbell-Moore", + ], + } + + +class SolutionThumbnail(Thumbnail): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "label_text": "$100^{d}$ kg", + }, + "collect_clack_data": False, + }, + } + + def add_text(self): + word = TextMobject("Solution") + question = TextMobject("How many collisions?") + word.set_width(7) + question.match_width(word) + question.next_to(word, UP) + group = VGroup(word, question) + group.to_edge(UP, buff=MED_LARGE_BUFF) + word.set_color(RED) + question.set_color(YELLOW) + group.set_stroke(RED, 2, background=True) + self.add(group) From c0bbe18a609c364184791dd320a2abd9974b5816 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 13:43:30 -0800 Subject: [PATCH 08/15] Added banner scene --- manimlib/for_3b1b_videos/common_scenes.py | 79 +++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/manimlib/for_3b1b_videos/common_scenes.py b/manimlib/for_3b1b_videos/common_scenes.py index be041d29..4eb1241e 100644 --- a/manimlib/for_3b1b_videos/common_scenes.py +++ b/manimlib/for_3b1b_videos/common_scenes.py @@ -293,3 +293,82 @@ class TODOStub(Scene): def construct(self): self.add(TextMobject("TODO: %s" % self.message)) self.wait() + + +class Banner(Scene): + CONFIG = { + "camera_config": { + "pixel_height": 1440, + "pixel_width": 2560, + }, + "pi_height": 1.25, + "pi_bottom": 0.25 * DOWN, + "use_date": False, + "date": "Sunday, February 3rd", + "message_scale_val": 0.9, + "add_supporter_note": False, + } + + def __init__(self, **kwargs): + # Force these dimensions + self.camera_config = { + "pixel_height": 1440, + "pixel_width": 2560, + } + Scene.__init__(self, **kwargs) + + def construct(self): + pis = self.get_pis() + pis.set_height(self.pi_height) + pis.arrange_submobjects(RIGHT, aligned_edge=DOWN) + pis.move_to(self.pi_bottom, DOWN) + self.add(pis) + + if self.use_date: + message = self.get_date_message() + else: + message = self.get_probabalistic_message() + message.scale(self.message_scale_val) + message.next_to(pis, DOWN) + self.add(message) + + if self.add_supporter_note: + note = self.get_supporter_note() + note.scale(0.5) + message.shift((MED_SMALL_BUFF - SMALL_BUFF) * UP) + note.next_to(message, DOWN, SMALL_BUFF) + self.add(note) + + yellow_parts = [sm for sm in message if sm.get_color() == YELLOW] + for pi in pis: + if yellow_parts: + pi.look_at(yellow_parts[-1]) + else: + pi.look_at(message) + + def get_pis(self): + return VGroup( + Randolph(color=BLUE_E, mode="pondering"), + Randolph(color=BLUE_D, mode="hooray"), + Randolph(color=BLUE_C, mode="sassy"), + Mortimer(color=GREY_BROWN, mode="thinking") + ) + + def get_probabalistic_message(self): + return TextMobject( + "New video every", "Sunday", + "(with probability 0.3)", + tex_to_color_map={"Sunday": YELLOW}, + ) + + def get_date_message(self): + return TextMobject( + "Next video on ", self.date, + tex_to_color_map={self.date: YELLOW}, + ) + + def get_supporter_note(self): + return TextMobject( + "(Available to supporters for review now)", + color="#F96854", + ) From 561e2c7a219731df94b490818f105a29216e45c1 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 13:44:19 -0800 Subject: [PATCH 09/15] Changed how partial movie directory is found --- manimlib/scene/scene.py | 2 +- manimlib/utils/output_directory_getters.py | 8 +++++--- stage_scenes.py | 1 - 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index 41e7ee57..e705a178 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -690,7 +690,7 @@ class Scene(Container): def get_partial_movie_directory(self): return get_partial_movie_output_directory( - self.__class__, self.camera_config, self.frame_duration + self, self.camera_config, self.frame_duration ) def open_movie_pipe(self): diff --git a/manimlib/utils/output_directory_getters.py b/manimlib/utils/output_directory_getters.py index a3695249..6f8cc674 100644 --- a/manimlib/utils/output_directory_getters.py +++ b/manimlib/utils/output_directory_getters.py @@ -34,13 +34,15 @@ def get_movie_output_directory(scene_class, camera_config, frame_duration): return guarantee_existance(os.path.join(directory, sub_dir)) -def get_partial_movie_output_directory(scene_class, camera_config, frame_duration): - directory = get_movie_output_directory(scene_class, camera_config, frame_duration) +def get_partial_movie_output_directory(scene, camera_config, frame_duration): + directory = get_movie_output_directory( + scene.__class__, camera_config, frame_duration + ) return guarantee_existance( os.path.join( directory, "partial_movie_files", - scene_class.__name__ + scene.get_output_file_name(), ) ) diff --git a/stage_scenes.py b/stage_scenes.py index 6d595ad5..7f894f9a 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -9,7 +9,6 @@ from manimlib.constants import PRODUCTION_QUALITY_FRAME_DURATION from manimlib.config import get_module from manimlib.extract_scene import is_child_scene from manimlib.utils.output_directory_getters import get_movie_output_directory -from manimlib.utils.output_directory_getters import get_partial_movie_output_directory from manimlib.utils.output_directory_getters import get_sorted_integer_files From 77f0ca1ad7ffae62f109eed1a8adc2b6fd9f1a54 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 13:45:04 -0800 Subject: [PATCH 10/15] Added clacks_names.py animatiosn --- active_projects/clacks.py | 14 ++++-- active_projects/clacks_names.py | 85 +++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 active_projects/clacks_names.py diff --git a/active_projects/clacks.py b/active_projects/clacks.py index 30842d06..327abed7 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks.py @@ -27,6 +27,8 @@ class Block(Square): self.fill_color = self.mass_to_color(self.mass) if self.label_text is None: self.label_text = self.mass_to_label_text(self.mass) + if "width" in kwargs: + kwargs.pop("width") Square.__init__(self, side_length=self.width, **kwargs) self.label = self.get_label() self.add(self.label) @@ -130,8 +132,8 @@ class SlidingBlocks(VGroup): def update_blocks_from_phase_space_point_tracker(self): block1, block2 = self.block1, self.block2 - ps_point = self.phase_space_point_tracker.get_location() + theta = np.arctan(np.sqrt(self.mass_ratio)) ps_point_angle = angle_of_vector(ps_point) n_clacks = int(ps_point_angle / theta) @@ -274,7 +276,6 @@ class BlocksAndWallScene(Scene): "collision_sound": "clack.wav", "show_flash_animations": True, "min_time_between_sounds": 0.004, - "allow_sound": True, } def setup(self): @@ -1560,7 +1561,7 @@ class EndScreen(Scene): ) -class Thumbnail(BlocksAndWallExample): +class Thumbnail(BlocksAndWallExample, MovingCameraScene): CONFIG = { "sliding_blocks_config": { "block1_config": { @@ -1572,10 +1573,15 @@ class Thumbnail(BlocksAndWallExample): "wait_time": 0, "count_clacks": False, "show_flash_animations": False, - "floor_y": -3, + "floor_y": -3.0, } + def setup(self): + MovingCameraScene.setup(self) + BlocksAndWallExample.setup(self) + def construct(self): + self.camera_frame.shift(0.9 * UP) self.thicken_lines() self.grow_labels() self.add_vector() diff --git a/active_projects/clacks_names.py b/active_projects/clacks_names.py new file mode 100644 index 00000000..ef0abbff --- /dev/null +++ b/active_projects/clacks_names.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +from big_ol_pile_of_manim_imports import * + +from active_projects.clacks import BlocksAndWallExample + + +class NameBump(BlocksAndWallExample): + CONFIG = { + "name": "Magnus Lysfjord", + "sliding_blocks_config": { + "block1_config": { + "mass": 1e6, + "velocity": -0.5, + "distance": 7, + }, + "block2_config": {}, + }, + "wait_time": 25, + } + + def setup(self): + names = self.name.split(" ") + n = len(names) + if n == 1: + names = 2 * [names[0]] + elif n > 2: + names = [ + " ".join(names[:n // 2]), + " ".join(names[n // 2:]), + ] + # Swap, to show first name on the left + names = [names[1], names[0]] + + name_mobs = VGroup(*map(TextMobject, names)) + name_mobs.set_stroke(BLACK, 3, background=True) + name_mobs.set_fill(LIGHT_GREY, 1) + name_mobs.set_sheen(3, UL) + name_mobs.scale(2) + configs = [ + self.sliding_blocks_config["block1_config"], + self.sliding_blocks_config["block2_config"], + ] + for name_mob, config in zip(name_mobs, configs): + config["width"] = name_mob.get_width() + self.name_mobs = name_mobs + + super().setup() + + def add_blocks(self): + super().add_blocks() + blocks = self.blocks + name_mobs = self.name_mobs + + blocks.fade(1) + + def update_name_mobs(name_mobs): + for name_mob, block in zip(name_mobs, self.blocks): + name_mob.move_to(block) + target_y = block.get_bottom()[1] + SMALL_BUFF + curr_y = name_mob[0].get_bottom()[1] + name_mob.shift((target_y - curr_y) * UP) + + name_mobs.add_updater(update_name_mobs) + self.add(name_mobs) + + clack_y = self.name_mobs[1].get_center()[1] + for location, time in self.clack_data: + location[1] = clack_y + + for block, name_mob in zip(blocks, name_mobs): + block.label.next_to(name_mob, UP) + block.label.set_fill(YELLOW, opacity=1) + + + +# for name in names: +# file_name = name.replace(".", "") +# file_name += " Name Bump" +# scene = NameBump( +# name=name, +# write_to_movie=True, +# output_file_name=file_name, +# camera_config=PRODUCTION_QUALITY_CAMERA_CONFIG, +# frame_duration=PRODUCTION_QUALITY_FRAME_DURATION, +# ) From 8ae055639457c0d76be99cf7bbd474eef5b25add Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 21:47:40 -0800 Subject: [PATCH 11/15] First pass at SceneFileWriter refactor --- .../clacks/all_questions_scenes.py | 8 + .../{clacks_names.py => clacks/name_bump.py} | 3 +- .../{clacks.py => clacks/question.py} | 5 +- .../clacks/question/480p15/NameIntro.mp4 | Bin 0 -> 38500 bytes .../NameIntro/00000.mp4 | Bin 0 -> 11456 bytes .../NameIntro/00001.mp4 | Bin 0 -> 11285 bytes .../NameIntro/00002.mp4 | Bin 0 -> 8645 bytes .../NameIntro/00003.mp4 | Bin 0 -> 9475 bytes .../NameIntro/partial_movie_file_list.txt | 4 + .../solution1.py} | 7 +- big_ol_pile_of_manim_imports.py | 2 +- manimlib/config.py | 39 +-- manimlib/constants.py | 1 + manimlib/extract_scene.py | 60 ++-- manimlib/scene/scene.py | 305 ++-------------- manimlib/scene/scene_file_writer.py | 325 ++++++++++++++++++ ...utput_directory_getters.py => file_ops.py} | 54 ++- stage_scenes.py | 3 +- 18 files changed, 432 insertions(+), 384 deletions(-) create mode 100644 active_projects/clacks/all_questions_scenes.py rename active_projects/{clacks_names.py => clacks/name_bump.py} (97%) rename active_projects/{clacks.py => clacks/question.py} (99%) create mode 100644 active_projects/clacks/question/480p15/NameIntro.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00000.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00001.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00002.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/partial_movie_file_list.txt rename active_projects/{clacks_solution1.py => clacks/solution1.py} (99%) create mode 100644 manimlib/scene/scene_file_writer.py rename manimlib/utils/{output_directory_getters.py => file_ops.py} (61%) diff --git a/active_projects/clacks/all_questions_scenes.py b/active_projects/clacks/all_questions_scenes.py new file mode 100644 index 00000000..c29d03ce --- /dev/null +++ b/active_projects/clacks/all_questions_scenes.py @@ -0,0 +1,8 @@ +from active_projects import clacks + +output_directory = "clacks_question" +all_scenes = [ + clacks.NameIntro, + clacks.MathAndPhysicsConspiring, + clacks.LightBouncing, +] diff --git a/active_projects/clacks_names.py b/active_projects/clacks/name_bump.py similarity index 97% rename from active_projects/clacks_names.py rename to active_projects/clacks/name_bump.py index ef0abbff..7ef69b59 100644 --- a/active_projects/clacks_names.py +++ b/active_projects/clacks/name_bump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from big_ol_pile_of_manim_imports import * -from active_projects.clacks import BlocksAndWallExample +from active_projects.clacks.question import BlocksAndWallExample class NameBump(BlocksAndWallExample): @@ -72,7 +72,6 @@ class NameBump(BlocksAndWallExample): block.label.set_fill(YELLOW, opacity=1) - # for name in names: # file_name = name.replace(".", "") # file_name += " Name Bump" diff --git a/active_projects/clacks.py b/active_projects/clacks/question.py similarity index 99% rename from active_projects/clacks.py rename to active_projects/clacks/question.py index 327abed7..71701e4b 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks/question.py @@ -346,10 +346,7 @@ class BlocksAndWallScene(Scene): self.counter_mob.set_value(n_clacks) def create_sound_file(self, clack_data): - directory = get_scene_output_directory(BlocksAndWallScene) - clack_file = os.path.join( - directory, 'sounds', self.collision_sound, - ) + clack_file = os.path.join(SOUND_DIR, self.collision_sound) output_file = self.get_movie_file_path(extension='.wav') times = [ time diff --git a/active_projects/clacks/question/480p15/NameIntro.mp4 b/active_projects/clacks/question/480p15/NameIntro.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ac3fb91eb490cd31a79f57e34c4578e634681927 GIT binary patch literal 38500 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

J6j=RLh-9?TbnHfB(SwUM`_oh!?1dHG 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 S1DG#T_Nthql$fN1#Qy-&Bb0Oi literal 0 HcmV?d00001 diff --git a/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 b/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..4b87ccf12d385e6e891d0bb91278dae9f71890a4 GIT binary patch literal 9475 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 b0GWWzOBfIgC`b?D? 0: - segment = segment.append( - AudioSegment.silent(int(np.ceil(diff * 1000))), - crossfade=0, - ) - self.audio_segment = segment.overlay( - new_segment, position=int(1000 * overly_time) - ) - - def add_sound(self, sound_file, time_offset=0): - new_segment = AudioSegment.from_file(sound_file) - self.add_audio_segment(new_segment, 0) + # TODO, Scene file writer now handles sound # Only these methods should touch the camera - def set_camera(self, camera): self.camera = camera @@ -202,9 +140,9 @@ class Scene(Container): mobjects=None, background=None, include_submobjects=True, - dont_update_when_skipping=True, + ignore_skipping=True, **kwargs): - if self.skip_animations and dont_update_when_skipping: + if self.skip_animations and not ignore_skipping: return if mobjects is None: mobjects = list_update( @@ -522,8 +460,6 @@ class Scene(Container): @handle_play_like_call def play(self, *args, **kwargs): - if self.livestreaming: - self.stream_lock = False if len(args) == 0: warnings.warn("Called Scene.play with no animations") return @@ -558,22 +494,11 @@ class Scene(Container): else: self.continual_update(0) - if self.livestreaming: - self.stream_lock = True - thread.start_new_thread(self.idle_stream, ()) return self + # TODO def idle_stream(self): - while(self.stream_lock): - a = datetime.datetime.now() - self.update_frame() - n_frames = 1 - frame = self.get_frame() - self.add_frames(*[frame] * n_frames) - b = datetime.datetime.now() - time_diff = (b - a).total_seconds() - if time_diff < self.frame_duration: - sleep(self.frame_duration - time_diff) + self.file_writer.idle_stream() def clean_up_animations(self, *animations): for animation in animations: @@ -638,202 +563,16 @@ class Scene(Container): return self def add_frames(self, *frames): + self.increment_time(len(frames) * self.frame_duration) if self.skip_animations: return - self.increment_time(len(frames) * self.frame_duration) - if self.write_to_movie: - for frame in frames: - if self.save_pngs: - self.save_image( - "frame" + str(self.frame_num), self.pngs_mode, True - ) - self.frame_num = self.frame_num + 1 - self.writing_process.stdin.write(frame.tostring()) - - # Display methods + for frame in frames: + self.file_writer.write_frame(frame) def show_frame(self): - self.update_frame(dont_update_when_skipping=False) + self.update_frame(ignore_skipping=True) self.get_image().show() - def get_image_file_path(self, name=None, dont_update=False): - sub_dir = "images" - output_file_name = self.get_output_file_name() - if dont_update: - sub_dir = output_file_name - path = get_image_output_directory(self.__class__, sub_dir) - file_name = add_extension_if_not_present( - name or output_file_name, ".png" - ) - return os.path.join(path, file_name) - - def save_image(self, name=None, mode="RGB", dont_update=False): - path = self.get_image_file_path(name, dont_update) - if not dont_update: - self.update_frame(dont_update_when_skipping=False) - image = self.get_image() - image = image.convert(mode) - image.save(path) - - def get_movie_file_path(self, name=None, extension=None): - directory = get_movie_output_directory( - self.__class__, self.camera_config, self.frame_duration - ) - if extension is None: - extension = self.movie_file_extension - if name is None: - name = self.get_output_file_name() - file_path = os.path.join(directory, name) - if not file_path.endswith(extension): - file_path += extension - return file_path - - def get_partial_movie_directory(self): - return get_partial_movie_output_directory( - self, self.camera_config, self.frame_duration - ) - - def open_movie_pipe(self): - directory = self.get_partial_movie_directory() - file_path = os.path.join( - directory, "{}{}".format( - self.num_plays, - self.movie_file_extension, - ) - ) - temp_file_path = file_path.replace(".", "_temp.") - - self.movie_file_path = file_path - self.temp_movie_file_path = temp_file_path - - fps = int(1 / self.frame_duration) - height = self.camera.get_pixel_height() - width = self.camera.get_pixel_width() - - command = [ - FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'rawvideo', - '-s', '%dx%d' % (width, height), # size of one frame - '-pix_fmt', 'rgba', - '-r', str(fps), # frames per second - '-i', '-', # The imput comes from a pipe - '-c:v', 'h264_nvenc', - '-an', # Tells FFMPEG not to expect any audio - '-loglevel', 'error', - ] - if self.movie_file_extension == ".mov": - # This is if the background of the exported video - # should be transparent. - command += [ - '-vcodec', 'qtrle', - # '-vcodec', 'png', - ] - else: - command += [ - '-vcodec', 'libx264', - '-pix_fmt', 'yuv420p', - ] - if self.livestreaming: - if self.to_twitch: - command += ['-f', 'flv'] - command += ['rtmp://live.twitch.tv/app/' + self.twitch_key] - else: - command += ['-f', 'mpegts'] - command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] - else: - command += [temp_file_path] - self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE) - - def close_movie_pipe(self): - self.writing_process.stdin.close() - self.writing_process.wait() - if self.livestreaming: - return True - shutil.move( - self.temp_movie_file_path, - self.movie_file_path, - ) - - def combine_movie_files(self): - # Manim renders the scene as many smaller movie files - # which are then concatenated to a larger one. The reason - # for this is that sometimes video-editing is made easier when - # one works with the broken up scene, which effectively has - # cuts at all the places you might want. But for viewing - # the scene as a whole, one of course wants to see it as a - # single piece. - partial_movie_file_directory = self.get_partial_movie_directory() - kwargs = { - "remove_non_integer_files": True, - "extension": self.movie_file_extension, - } - if self.start_at_animation_number is not None: - kwargs["min_index"] = self.start_at_animation_number - if self.end_at_animation_number is not None: - kwargs["max_index"] = self.end_at_animation_number - else: - kwargs["remove_indices_greater_than"] = self.num_plays - 1 - partial_movie_files = get_sorted_integer_files( - partial_movie_file_directory, - **kwargs - ) - # Write a file partial_file_list.txt containing all - # partial movie files - file_list = os.path.join( - partial_movie_file_directory, - "partial_movie_file_list.txt" - ) - with open(file_list, 'w') as fp: - for pf_path in partial_movie_files: - if os.name == 'nt': - pf_path = pf_path.replace('\\', '/') - fp.write("file \'{}\'\n".format(pf_path)) - - movie_file_path = self.get_movie_file_path() - commands = [ - FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'concat', - '-safe', '0', - '-i', file_list, - '-c', 'copy', - '-loglevel', 'error', - movie_file_path - ] - if not self.includes_sound: - commands.insert(-1, '-an') - - combine_process = subprocess.Popen(commands) - combine_process.wait() - # os.remove(file_list) - - if self.includes_sound: - sound_file_path = movie_file_path.replace( - self.movie_file_extension, ".wav" - ) - # Makes sure sound file length will match video file - self.add_audio_segment(AudioSegment.silent(0)) - self.audio_segment.export(sound_file_path) - temp_file_path = movie_file_path.replace(".", "_temp.") - commands = commands = [ - "ffmpeg", - "-i", movie_file_path, - "-i", sound_file_path, - '-y', # overwrite output file if it exists - "-c:v", "copy", "-c:a", "aac", - '-loglevel', 'error', - "-shortest", - "-strict", "experimental", - temp_file_path, - ] - subprocess.call(commands) - shutil.move(temp_file_path, movie_file_path) - # subprocess.call(["rm", self.temp_movie_file_path]) - subprocess.call(["rm", sound_file_path]) - - print("\nAnimation ready at {}\n".format(movie_file_path)) - # TODO, this doesn't belong in Scene, but should be # part of some more specialized subclass optimized # for livestreaming diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py new file mode 100644 index 00000000..4cc56bef --- /dev/null +++ b/manimlib/scene/scene_file_writer.py @@ -0,0 +1,325 @@ +import numpy as np +from pydub import AudioSegment +import shutil +import subprocess +import os +import _thread as thread +from time import sleep +import datetime + +from manimlib.constants import FFMPEG_BIN +from manimlib.constants import STREAMING_IP +from manimlib.constants import STREAMING_PORT +from manimlib.constants import STREAMING_PROTOCOL +from manimlib.constants import VIDEO_DIR +from manimlib.utils.config_ops import digest_config +from manimlib.utils.file_ops import guarantee_existance +from manimlib.utils.file_ops import add_extension_if_not_present +from manimlib.utils.file_ops import get_sorted_integer_files + + +class SceneFileWriter(object): + CONFIG = { + "write_to_movie": False, + # TODO, save_pngs is doing nothing + "save_pngs": False, + "png_mode": "RGBA", + "save_last_frame": False, + "movie_file_extension": ".mp4", + "livestreaming": False, + "to_twitch": False, + "twitch_key": None, + # Previous output_file_name + # TODO, address this in extract_scene et. al. + "file_name": None, + "output_directory": None, + } + + def __init__(self, scene, **kwargs): + digest_config(self, kwargs) + self.scene = scene + self.init_audio() + self.init_output_directories() + self.stream_lock = False + + # Output directories and files + + def init_output_directories(self): + output_directory = self.output_directory or self.get_default_output_directory() + file_name = self.file_name or self.get_default_file_name() + if self.save_last_frame: + image_dir = guarantee_existance(os.path.join( + VIDEO_DIR, + output_directory, + self.get_image_directory(), + )) + self.image_file_path = os.path.join( + image_dir, + add_extension_if_not_present(file_name, ".png") + ) + if self.write_to_movie: + movie_dir = guarantee_existance(os.path.join( + output_directory, + self.get_movie_directory(), + )) + self.movie_file_path = os.path.join( + movie_dir, + add_extension_if_not_present( + file_name, self.movie_file_extension + ) + ) + self.partial_movie_directory = guarantee_existance(os.path.join( + movie_dir, + self.get_partial_movie_directory(), + file_name, + )) + + def get_default_output_directory(self): + scene_module = self.scene.__class__.__module__ + return scene_module.replace(".", os.path.sep) + + def get_default_file_name(self): + return self.scene.__class__.__name__ + + def get_movie_directory(self): + pixel_height = self.scene.camera.pixel_height + frame_duration = self.scene.frame_duration + return "{}p{}".format( + pixel_height, int(1.0 / frame_duration) + ) + + def get_image_directory(self): + return "images" + + def get_partial_movie_directory(self): + return "partial_movie_directory" + + # Sound + # TODO, make work with Scene + def init_audio(self): + self.includes_sound = False + + def create_audio_segment(self): + self.audio_segment = AudioSegment.silent() + + def add_audio_segment(self, new_segment, time_offset=0): + if not self.includes_sound: + self.includes_sound = True + self.create_audio_segment() + segment = self.audio_segment + overly_time = self.get_time() + time_offset + if overly_time < 0: + raise Exception("Adding sound at timestamp < 0") + + curr_end = segment.duration_seconds + new_end = overly_time + new_segment.duration_seconds + diff = new_end - curr_end + if diff > 0: + segment = segment.append( + AudioSegment.silent(int(np.ceil(diff * 1000))), + crossfade=0, + ) + self.audio_segment = segment.overlay( + new_segment, position=int(1000 * overly_time) + ) + + def add_sound(self, sound_file, time_offset=0): + new_segment = AudioSegment.from_file(sound_file) + self.add_audio_segment(new_segment, 0) + + # Directory getters + def get_image_file_path(self): + return self.image_file_path + + def get_next_partial_movie_path(self): + result = os.path.join( + self.partial_movie_directory, + "{:05}{}".format( + self.scene.num_plays, + self.movie_file_extension, + ) + ) + return result + + def get_movie_file_path(self): + return self.movie_file_path + + # Writers + def write_frame(self, frame): + if self.write_to_movie: + self.writing_process.stdin.write(frame.tostring()) + + def save_image(self, image): + file_path = self.get_image_file_path() + image.save(file_path) + self.print_file_ready_message(file_path) + + def begin_animation(self, allow_write=False): + if self.write_to_movie and allow_write: + self.open_movie_pipe() + if self.livestreaming: + self.stream_lock = False + + def end_animation(self, allow_write=False): + if self.write_to_movie and allow_write: + self.close_movie_pipe() + if self.livestreaming: + self.stream_lock = True + thread.start_new_thread(self.idle_stream, ()) + + def idle_stream(self): + while self.stream_lock: + a = datetime.datetime.now() + self.update_frame() + n_frames = 1 + frame = self.get_frame() + self.add_frames(*[frame] * n_frames) + b = datetime.datetime.now() + time_diff = (b - a).total_seconds() + if time_diff < self.frame_duration: + sleep(self.frame_duration - time_diff) + + def finish(self): + if self.write_to_movie: + if hasattr(self, "writing_process"): + self.writing_process.terminate() + self.combine_movie_files() + if self.save_last_frame: + self.scene.update_frame(ignore_skipping=True) + self.save_image(self.scene.get_image()) + + def open_movie_pipe(self): + file_path = self.get_next_partial_movie_path() + temp_file_path = file_path.replace(".", "_temp.") + + self.partial_movie_file_path = file_path + self.temp_partial_movie_file_path = temp_file_path + + fps = int(1 / self.scene.frame_duration) + height = self.scene.camera.get_pixel_height() + width = self.scene.camera.get_pixel_width() + + command = [ + FFMPEG_BIN, + '-y', # overwrite output file if it exists + '-f', 'rawvideo', + '-s', '%dx%d' % (width, height), # size of one frame + '-pix_fmt', 'rgba', + '-r', str(fps), # frames per second + '-i', '-', # The imput comes from a pipe + '-c:v', 'h264_nvenc', + '-an', # Tells FFMPEG not to expect any audio + '-loglevel', 'error', + ] + if self.movie_file_extension == ".mov": + # This is if the background of the exported video + # should be transparent. + command += [ + '-vcodec', 'qtrle', + # '-vcodec', 'png', + ] + else: + command += [ + '-vcodec', 'libx264', + '-pix_fmt', 'yuv420p', + ] + if self.livestreaming: + if self.to_twitch: + command += ['-f', 'flv'] + command += ['rtmp://live.twitch.tv/app/' + self.twitch_key] + else: + command += ['-f', 'mpegts'] + command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] + else: + command += [temp_file_path] + self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE) + + def close_movie_pipe(self): + self.writing_process.stdin.close() + self.writing_process.wait() + if self.livestreaming: + return True + shutil.move( + self.temp_partial_movie_file_path, + self.partial_movie_file_path, + ) + + def combine_movie_files(self): + # Manim renders the scene as many smaller movie files + # which are then concatenated to a larger one. The reason + # for this is that sometimes video-editing is made easier when + # one works with the broken up scene, which effectively has + # cuts at all the places you might want. But for viewing + # the scene as a whole, one of course wants to see it as a + # single piece. + kwargs = { + "remove_non_integer_files": True, + "extension": self.movie_file_extension, + } + if self.scene.start_at_animation_number is not None: + kwargs["min_index"] = self.start_at_animation_number + if self.scene.end_at_animation_number is not None: + kwargs["max_index"] = self.end_at_animation_number + else: + kwargs["remove_indices_greater_than"] = self.scene.num_plays - 1 + partial_movie_files = get_sorted_integer_files( + self.partial_movie_directory, + **kwargs + ) + # Write a file partial_file_list.txt containing all + # partial movie files + file_list = os.path.join( + self.partial_movie_directory, + "partial_movie_file_list.txt" + ) + with open(file_list, 'w') as fp: + for pf_path in partial_movie_files: + if os.name == 'nt': + pf_path = pf_path.replace('\\', '/') + fp.write("file \'{}\'\n".format(pf_path)) + + movie_file_path = self.get_movie_file_path() + commands = [ + FFMPEG_BIN, + '-y', # overwrite output file if it exists + '-f', 'concat', + '-safe', '0', + '-i', file_list, + '-c', 'copy', + '-loglevel', 'error', + movie_file_path + ] + if not self.includes_sound: + commands.insert(-1, '-an') + + combine_process = subprocess.Popen(commands) + combine_process.wait() + # os.remove(file_list) + + if self.includes_sound: + sound_file_path = movie_file_path.replace( + self.movie_file_extension, ".wav" + ) + # Makes sure sound file length will match video file + self.add_audio_segment(AudioSegment.silent(0)) + self.audio_segment.export(sound_file_path) + temp_file_path = movie_file_path.replace(".", "_temp.") + commands = commands = [ + "ffmpeg", + "-i", movie_file_path, + "-i", sound_file_path, + '-y', # overwrite output file if it exists + "-c:v", "copy", "-c:a", "aac", + '-loglevel', 'error', + "-shortest", + "-strict", "experimental", + temp_file_path, + ] + subprocess.call(commands) + shutil.move(temp_file_path, movie_file_path) + subprocess.call(["rm", sound_file_path]) + + self.print_file_ready_message(movie_file_path) + + def print_file_ready_message(self, file_path): + print("\nFile ready at {}\n".format(file_path)) diff --git a/manimlib/utils/output_directory_getters.py b/manimlib/utils/file_ops.py similarity index 61% rename from manimlib/utils/output_directory_getters.py rename to manimlib/utils/file_ops.py index 6f8cc674..c06bd148 100644 --- a/manimlib/utils/output_directory_getters.py +++ b/manimlib/utils/file_ops.py @@ -1,8 +1,6 @@ import os import numpy as np -from manimlib.constants import VIDEO_DIR - def add_extension_if_not_present(file_name, extension): # This could conceivably be smarter about handling existing differing extensions @@ -18,38 +16,38 @@ def guarantee_existance(path): return os.path.abspath(path) -def get_scene_output_directory(scene_class): - return guarantee_existance(os.path.join( - VIDEO_DIR, - scene_class.__module__.replace(".", os.path.sep) - )) +# def get_scene_output_directory(scene_class): +# return guarantee_existance(os.path.join( +# VIDEO_DIR, +# scene_class.__module__.replace(".", os.path.sep) +# )) -def get_movie_output_directory(scene_class, camera_config, frame_duration): - directory = get_scene_output_directory(scene_class) - sub_dir = "%dp%d" % ( - camera_config["pixel_height"], - int(1.0 / frame_duration) - ) - return guarantee_existance(os.path.join(directory, sub_dir)) +# def get_movie_output_directory(scene_class, camera_config, frame_duration): +# directory = get_scene_output_directory(scene_class) +# sub_dir = "%dp%d" % ( +# camera_config["pixel_height"], +# int(1.0 / frame_duration) +# ) +# return guarantee_existance(os.path.join(directory, sub_dir)) -def get_partial_movie_output_directory(scene, camera_config, frame_duration): - directory = get_movie_output_directory( - scene.__class__, camera_config, frame_duration - ) - return guarantee_existance( - os.path.join( - directory, - "partial_movie_files", - scene.get_output_file_name(), - ) - ) +# def get_partial_movie_output_directory(scene, camera_config, frame_duration): +# directory = get_movie_output_directory( +# scene.__class__, camera_config, frame_duration +# ) +# return guarantee_existance( +# os.path.join( +# directory, +# "partial_movie_files", +# scene.get_output_file_name(), +# ) +# ) -def get_image_output_directory(scene_class, sub_dir="images"): - directory = get_scene_output_directory(scene_class) - return guarantee_existance(os.path.join(directory, sub_dir)) +# def get_image_output_directory(scene_class, sub_dir="images"): +# directory = get_scene_output_directory(scene_class) +# return guarantee_existance(os.path.join(directory, sub_dir)) def get_sorted_integer_files(directory, diff --git a/stage_scenes.py b/stage_scenes.py index 7f894f9a..65e055aa 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -8,8 +8,7 @@ from manimlib.constants import PRODUCTION_QUALITY_CAMERA_CONFIG from manimlib.constants import PRODUCTION_QUALITY_FRAME_DURATION from manimlib.config import get_module from manimlib.extract_scene import is_child_scene -from manimlib.utils.output_directory_getters import get_movie_output_directory -from manimlib.utils.output_directory_getters import get_sorted_integer_files +from manimlib.utils.file_ops import get_movie_output_directory def get_sorted_scene_classes(module_name): From e5e1fa908bedcfbac0d2a49a2e104f264f2f3574 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 22:24:01 -0800 Subject: [PATCH 12/15] Finished SceneFileWriter refactor --- README.md | 2 +- .../clacks/question/480p15/NameIntro.mp4 | Bin 38500 -> 0 bytes .../NameIntro/00000.mp4 | Bin 11456 -> 0 bytes .../NameIntro/00001.mp4 | Bin 11285 -> 0 bytes .../NameIntro/00002.mp4 | Bin 8645 -> 0 bytes .../NameIntro/00003.mp4 | Bin 9475 -> 0 bytes .../NameIntro/partial_movie_file_list.txt | 4 - example_scenes.py | 5 +- manimlib/config.py | 4 +- manimlib/scene/scene.py | 31 +++--- manimlib/scene/scene_file_writer.py | 92 +++++++++--------- manimlib/utils/file_ops.py | 42 ++------ manimlib/utils/images.py | 15 +-- manimlib/utils/sounds.py | 10 ++ 14 files changed, 94 insertions(+), 111 deletions(-) delete mode 100644 active_projects/clacks/question/480p15/NameIntro.mp4 delete mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00000.mp4 delete mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00001.mp4 delete mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00002.mp4 delete mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 delete mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/partial_movie_file_list.txt diff --git a/README.md b/README.md index 72846d99..69300a70 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Set MEDIA_DIR environment variable to determine where image and animation files Look through the old_projects folder to see the code for previous 3b1b videos. Note, however, that developments are often made to the library without considering backwards compatibility on those old_projects. To run them with a guarantee that they will work, you will have to go back to the commit which complete that project. -While developing a scene, the `-s` flag is helpful to just see what things look like at the end without having to generate the full animation. It can also be helpful to use the `-n` flag to skip over some number of animations. +While developing a scene, the `-sp` flags are helpful to just see what things look like at the end without having to generate the full animation. It can also be helpful to use the `-n` flag to skip over some number of animations. ### Documentation Documentation is in progress at [manim.readthedocs.io](https://manim.readthedocs.io). diff --git a/active_projects/clacks/question/480p15/NameIntro.mp4 b/active_projects/clacks/question/480p15/NameIntro.mp4 deleted file mode 100644 index ac3fb91eb490cd31a79f57e34c4578e634681927..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 38500 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

J6j=RLh-9?TbnHfB(SwUM`_oh!?1dHG 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 S1DG#T_Nthql$fN1#Qy-&Bb0Oi diff --git a/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 b/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 deleted file mode 100644 index 4b87ccf12d385e6e891d0bb91278dae9f71890a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9475 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 b0GWWzOBfIgC`b?D? to skip ahead to the n'th animation of a scene. diff --git a/manimlib/config.py b/manimlib/config.py index ee632759..db415cef 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -25,7 +25,7 @@ def parse_cli(): parser.add_argument( "-p", "--preview", action="store_true", - help="Automatically open movie file once its done", + help="Automatically open the saved file once its done", ), parser.add_argument( "-w", "--write_to_movie", @@ -35,7 +35,7 @@ def parse_cli(): parser.add_argument( "-s", "--save_last_frame", action="store_true", - help="Save the last frame and open the image file", + help="Save the last frame", ), parser.add_argument( "-l", "--low_quality", diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index c0b0aed9..009bfd98 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -33,7 +33,6 @@ class Scene(Container): } def __init__(self, **kwargs): - # Perhaps allow passing in a non-empty *mobjects parameter? Container.__init__(self, **kwargs) self.camera = self.camera_class(**self.camera_config) self.file_writer = SceneFileWriter( @@ -42,6 +41,7 @@ class Scene(Container): self.mobjects = [] self.continual_animations = [] + # TODO, remove need for foreground mobjects self.foreground_mobjects = [] self.num_plays = 0 self.time = 0 @@ -59,16 +59,6 @@ class Scene(Container): self.file_writer.finish() self.print_end_message() - def handle_play_like_call(func): - def wrapper(self, *args, **kwargs): - self.handle_animation_skipping() - allow_write = not self.skip_animations - self.file_writer.begin_animation(allow_write) - func(self, *args, **kwargs) - self.file_writer.end_animation(allow_write) - self.num_plays += 1 - return wrapper - def setup(self): """ This is meant to be implement by any scenes which @@ -111,8 +101,6 @@ class Scene(Container): def get_attrs(self, *keys): return [getattr(self, key) for key in keys] - # TODO, Scene file writer now handles sound - # Only these methods should touch the camera def set_camera(self, camera): self.camera = camera @@ -449,7 +437,7 @@ class Scene(Container): compile_method(state) return animations - def handle_animation_skipping(self): + def update_skipping_status(self): if self.start_at_animation_number: if self.num_plays == self.start_at_animation_number: self.skip_animations = False @@ -458,6 +446,16 @@ class Scene(Container): self.skip_animations = True raise EndSceneEarlyException() + def handle_play_like_call(func): + def wrapper(self, *args, **kwargs): + self.update_skipping_status() + allow_write = not self.skip_animations + self.file_writer.begin_animation(allow_write) + func(self, *args, **kwargs) + self.file_writer.end_animation(allow_write) + self.num_plays += 1 + return wrapper + @handle_play_like_call def play(self, *args, **kwargs): if len(args) == 0: @@ -496,7 +494,6 @@ class Scene(Container): return self - # TODO def idle_stream(self): self.file_writer.idle_stream() @@ -569,6 +566,10 @@ class Scene(Container): for frame in frames: self.file_writer.write_frame(frame) + def add_sound(self, sound_file, time_offset=0): + time = self.get_time() + time_offset + self.file_writer.add_sound(sound_file, time) + def show_frame(self): self.update_frame(ignore_skipping=True) self.get_image().show() diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py index 4cc56bef..a4fd8878 100644 --- a/manimlib/scene/scene_file_writer.py +++ b/manimlib/scene/scene_file_writer.py @@ -16,6 +16,7 @@ from manimlib.utils.config_ops import digest_config from manimlib.utils.file_ops import guarantee_existance from manimlib.utils.file_ops import add_extension_if_not_present from manimlib.utils.file_ops import get_sorted_integer_files +from manimlib.utils.sounds import get_full_sound_file_path class SceneFileWriter(object): @@ -38,12 +39,11 @@ class SceneFileWriter(object): def __init__(self, scene, **kwargs): digest_config(self, kwargs) self.scene = scene - self.init_audio() - self.init_output_directories() self.stream_lock = False + self.init_output_directories() + self.init_audio() # Output directories and files - def init_output_directories(self): output_directory = self.output_directory or self.get_default_output_directory() file_name = self.file_name or self.get_default_file_name() @@ -59,6 +59,7 @@ class SceneFileWriter(object): ) if self.write_to_movie: movie_dir = guarantee_existance(os.path.join( + VIDEO_DIR, output_directory, self.get_movie_directory(), )) @@ -94,39 +95,6 @@ class SceneFileWriter(object): def get_partial_movie_directory(self): return "partial_movie_directory" - # Sound - # TODO, make work with Scene - def init_audio(self): - self.includes_sound = False - - def create_audio_segment(self): - self.audio_segment = AudioSegment.silent() - - def add_audio_segment(self, new_segment, time_offset=0): - if not self.includes_sound: - self.includes_sound = True - self.create_audio_segment() - segment = self.audio_segment - overly_time = self.get_time() + time_offset - if overly_time < 0: - raise Exception("Adding sound at timestamp < 0") - - curr_end = segment.duration_seconds - new_end = overly_time + new_segment.duration_seconds - diff = new_end - curr_end - if diff > 0: - segment = segment.append( - AudioSegment.silent(int(np.ceil(diff * 1000))), - crossfade=0, - ) - self.audio_segment = segment.overlay( - new_segment, position=int(1000 * overly_time) - ) - - def add_sound(self, sound_file, time_offset=0): - new_segment = AudioSegment.from_file(sound_file) - self.add_audio_segment(new_segment, 0) - # Directory getters def get_image_file_path(self): return self.image_file_path @@ -144,16 +112,41 @@ class SceneFileWriter(object): def get_movie_file_path(self): return self.movie_file_path + # Sound + def init_audio(self): + self.includes_sound = False + + def create_audio_segment(self): + self.audio_segment = AudioSegment.silent() + + def add_audio_segment(self, new_segment, time=None): + if not self.includes_sound: + self.includes_sound = True + self.create_audio_segment() + segment = self.audio_segment + curr_end = segment.duration_seconds + if time is None: + time = curr_end + if time < 0: + raise Exception("Adding sound at timestamp < 0") + + new_end = time + new_segment.duration_seconds + diff = new_end - curr_end + if diff > 0: + segment = segment.append( + AudioSegment.silent(int(np.ceil(diff * 1000))), + crossfade=0, + ) + self.audio_segment = segment.overlay( + new_segment, position=int(1000 * time) + ) + + def add_sound(self, sound_file, time): + file_path = get_full_sound_file_path(sound_file) + new_segment = AudioSegment.from_file(file_path) + self.add_audio_segment(new_segment, time) + # Writers - def write_frame(self, frame): - if self.write_to_movie: - self.writing_process.stdin.write(frame.tostring()) - - def save_image(self, image): - file_path = self.get_image_file_path() - image.save(file_path) - self.print_file_ready_message(file_path) - def begin_animation(self, allow_write=False): if self.write_to_movie and allow_write: self.open_movie_pipe() @@ -167,6 +160,15 @@ class SceneFileWriter(object): self.stream_lock = True thread.start_new_thread(self.idle_stream, ()) + def write_frame(self, frame): + if self.write_to_movie: + self.writing_process.stdin.write(frame.tostring()) + + def save_image(self, image): + file_path = self.get_image_file_path() + image.save(file_path) + self.print_file_ready_message(file_path) + def idle_stream(self): while self.stream_lock: a = datetime.datetime.now() diff --git a/manimlib/utils/file_ops.py b/manimlib/utils/file_ops.py index c06bd148..36a21e4e 100644 --- a/manimlib/utils/file_ops.py +++ b/manimlib/utils/file_ops.py @@ -16,38 +16,16 @@ def guarantee_existance(path): return os.path.abspath(path) -# def get_scene_output_directory(scene_class): -# return guarantee_existance(os.path.join( -# VIDEO_DIR, -# scene_class.__module__.replace(".", os.path.sep) -# )) - - -# def get_movie_output_directory(scene_class, camera_config, frame_duration): -# directory = get_scene_output_directory(scene_class) -# sub_dir = "%dp%d" % ( -# camera_config["pixel_height"], -# int(1.0 / frame_duration) -# ) -# return guarantee_existance(os.path.join(directory, sub_dir)) - - -# def get_partial_movie_output_directory(scene, camera_config, frame_duration): -# directory = get_movie_output_directory( -# scene.__class__, camera_config, frame_duration -# ) -# return guarantee_existance( -# os.path.join( -# directory, -# "partial_movie_files", -# scene.get_output_file_name(), -# ) -# ) - - -# def get_image_output_directory(scene_class, sub_dir="images"): -# directory = get_scene_output_directory(scene_class) -# return guarantee_existance(os.path.join(directory, sub_dir)) +def seek_full_path_from_defaults(file_name, default_dir, extensions): + possible_paths = [file_name] + possible_paths += [ + os.path.join(default_dir, file_name + extension) + for extension in ["", *extensions] + ] + for path in possible_paths: + if os.path.exists(path): + return path + raise IOError("File {} not Found".format(file_name)) def get_sorted_integer_files(directory, diff --git a/manimlib/utils/images.py b/manimlib/utils/images.py index 57d089b4..63dcc58b 100644 --- a/manimlib/utils/images.py +++ b/manimlib/utils/images.py @@ -4,20 +4,15 @@ import os from PIL import Image from manimlib.constants import RASTER_IMAGE_DIR +from manimlib.utils.file_ops import seek_full_path_from_defaults def get_full_raster_image_path(image_file_name): - possible_paths = [ + return seek_full_path_from_defaults( image_file_name, - os.path.join(RASTER_IMAGE_DIR, image_file_name), - os.path.join(RASTER_IMAGE_DIR, image_file_name + ".jpg"), - os.path.join(RASTER_IMAGE_DIR, image_file_name + ".png"), - os.path.join(RASTER_IMAGE_DIR, image_file_name + ".gif"), - ] - for path in possible_paths: - if os.path.exists(path): - return path - raise IOError("File %s not Found" % image_file_name) + default_dir=RASTER_IMAGE_DIR, + extensions=[".jpg", ".png", ".gif"] + ) def drag_pixels(frames): diff --git a/manimlib/utils/sounds.py b/manimlib/utils/sounds.py index e9d3dc7e..17a185ce 100644 --- a/manimlib/utils/sounds.py +++ b/manimlib/utils/sounds.py @@ -1,4 +1,6 @@ import os +from manimlib.constants import SOUND_DIR +from manimlib.utils.file_ops import seek_full_path_from_defaults def play_chord(*nums): @@ -28,3 +30,11 @@ def play_error_sound(): def play_finish_sound(): play_chord(12, 9, 5, 2) + + +def get_full_sound_file_path(sound_file_name): + return seek_full_path_from_defaults( + sound_file_name, + default_dir=SOUND_DIR, + extensions=[".wav", ".mp3"] + ) From 14cd017cc4f7eb49f4d3338b7626d992adae36e4 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 22:26:18 -0800 Subject: [PATCH 13/15] partial_movie_directory -> partial_movie_files --- manimlib/scene/scene_file_writer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py index a4fd8878..f12a7531 100644 --- a/manimlib/scene/scene_file_writer.py +++ b/manimlib/scene/scene_file_writer.py @@ -93,7 +93,7 @@ class SceneFileWriter(object): return "images" def get_partial_movie_directory(self): - return "partial_movie_directory" + return "partial_movie_files" # Directory getters def get_image_file_path(self): From a5e25042e2e95a6d474cca6566fc15a2611a8842 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 22:28:11 -0800 Subject: [PATCH 14/15] Note on how sound aspect of BlocksAndWallScene was broken --- active_projects/clacks/question.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/active_projects/clacks/question.py b/active_projects/clacks/question.py index 71701e4b..17dab588 100644 --- a/active_projects/clacks/question.py +++ b/active_projects/clacks/question.py @@ -375,6 +375,8 @@ class BlocksAndWallScene(Scene): clacks.export(output_file, format="wav") return output_file + # 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: From eca8d77f83431f2260b7dc279d8bed6443ba0cbd Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 22:32:43 -0800 Subject: [PATCH 15/15] Small bug fix for starting_animation_number --- manimlib/scene/scene_file_writer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py index f12a7531..e19afc48 100644 --- a/manimlib/scene/scene_file_writer.py +++ b/manimlib/scene/scene_file_writer.py @@ -259,9 +259,9 @@ class SceneFileWriter(object): "extension": self.movie_file_extension, } if self.scene.start_at_animation_number is not None: - kwargs["min_index"] = self.start_at_animation_number + kwargs["min_index"] = self.scene.start_at_animation_number if self.scene.end_at_animation_number is not None: - kwargs["max_index"] = self.end_at_animation_number + kwargs["max_index"] = self.scene.end_at_animation_number else: kwargs["remove_indices_greater_than"] = self.scene.num_plays - 1 partial_movie_files = get_sorted_integer_files(