From 44f9954dbdc1009fb9dabea6575da6d386b420c3 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 18 Jan 2018 21:26:57 -0800 Subject: [PATCH 1/3] Enable -n command on extract_scene.py to skip to a given animation in a scene --- extract_scene.py | 15 ++++++++++++--- scene/scene.py | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/extract_scene.py b/extract_scene.py index 537884f7..455c7f25 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -66,6 +66,7 @@ def get_configuration(): for short_arg, long_arg in optional_args: parser.add_argument(short_arg, long_arg, action = "store_true") parser.add_argument("-o", "--output_name") + parser.add_argument("-n", "--skip_to_animation_number") args = parser.parse_args() except argparse.ArgumentError as err: print(str(err)) @@ -85,6 +86,7 @@ def get_configuration(): "quiet" : args.quiet or args.write_all, "write_all" : args.write_all, "output_name" : args.output_name, + "skip_to_animation_number" : args.skip_to_animation_number, } if args.low_quality or args.preview: config["camera_config"] = LOW_QUALITY_CAMERA_CONFIG @@ -96,12 +98,18 @@ def get_configuration(): config["camera_config"] = PRODUCTION_QUALITY_CAMERA_CONFIG config["frame_duration"] = PRODUCTION_QUALITY_FRAME_DURATION + stan = config["skip_to_animation_number"] + if stan is not None: + config["skip_to_animation_number"] = int(stan) + #By default, write to file actions = ["write_to_movie", "preview", "show_last_frame"] if not any([config[key] for key in actions]): config["write_to_movie"] = True - config["skip_animations"] = config["show_last_frame"] and not config["write_to_movie"] - + config["skip_animations"] = any([ + config["show_last_frame"] and not config["write_to_movie"], + config["skip_to_animation_number"], + ]) return config def handle_scene(scene, **config): @@ -203,7 +211,8 @@ def main(): "write_to_movie", "save_frames", "output_directory", - "save_pngs" + "save_pngs", + "skip_to_animation_number", ] ]) diff --git a/scene/scene.py b/scene/scene.py index eec090ea..84ea28c1 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -36,6 +36,7 @@ class Scene(object): "name" : None, "always_continually_update" : False, "random_seed" : 0, + "skip_to_animation_number" : None, } def __init__(self, **kwargs): digest_config(self, kwargs) @@ -59,6 +60,7 @@ class Scene(object): self.construct(*self.construct_args) if self.write_to_movie: self.close_movie_pipe() + print("Played a total of %d animations"%self.num_plays) def setup(self): """ @@ -389,9 +391,13 @@ class Scene(object): if len(args) == 0: warnings.warn("Called Scene.play with no animations") return + if self.skip_to_animation_number: + if self.num_plays + 1 == self.skip_to_animation_number: + self.skip_animations = False if self.skip_animations: kwargs["run_time"] = 0 + animations = self.compile_play_args_to_animation_list(*args) self.num_plays += 1 From 22d6d2dba588520704894d328e8e568a48461047 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 19 Jan 2018 12:50:05 -0800 Subject: [PATCH 2/3] Further fourier work --- active_projects/fourier.py | 79 +++++++++++++++++++++++++++++++------- scene/scene.py | 1 - 2 files changed, 66 insertions(+), 14 deletions(-) diff --git a/active_projects/fourier.py b/active_projects/fourier.py index 63f4c7a7..6537cc2f 100644 --- a/active_projects/fourier.py +++ b/active_projects/fourier.py @@ -387,7 +387,7 @@ class AddingPureFrequencies(PiCreatureScene): buff = 0, ) return result - + def stack_v_lines(self, x, lines): point = self.axes.coords_to_point(x, self.equilibrium_height) A_line, D_line = lines @@ -911,8 +911,8 @@ class FourierMachineScene(Scene): self.remove(v_line.polarized_mobject) self.play(FadeOut(VGroup(v_line, v_line.polarized_mobject))) - def get_v_lines_indicating_periods(self, freq, n_lines = 10): - period = np.divide(1., freq) + def get_v_lines_indicating_periods(self, freq, n_lines = 20): + period = np.divide(1., max(freq, 0.01)) v_lines = VGroup(*[ DashedLine(ORIGIN, 1.5*UP).move_to( self.time_axes.coords_to_point(n*period, 0), @@ -923,6 +923,17 @@ class FourierMachineScene(Scene): v_lines.set_stroke(LIGHT_GREY) return v_lines + def get_period_v_lines_update_anim(self): + def update_v_lines(v_lines): + freq = self.graph.polarized_mobject.frequency + Transform( + v_lines, + self.get_v_lines_indicating_periods(freq) + ).update(1) + return UpdateFromFunc( + self.v_lines_indicating_periods, update_v_lines + ) + class WrapCosineGraphAroundCircle(FourierMachineScene): CONFIG = { "initial_winding_frequency" : 0.5, @@ -1099,8 +1110,6 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene): "center_of_mass_color" : RED, } def construct(self): - self.force_skipping() - self.remove(self.pi_creature) self.setup_graph() # self.indicate_weight_of_wire() @@ -1108,6 +1117,8 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene): # self.change_to_various_frequencies() self.introduce_frequency_plot() self.draw_full_frequency_plot() + self.recap_objects_on_screen() + self.lower_graph() self.label_as_almost_fourier() def setup_graph(self): @@ -1219,7 +1230,6 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene): t_min = 0, t_max = TAU, ) - self.revert_to_original_skipping_status() self.play( wps_label.move_to, self.circle_plane.get_top(), com_label.move_to, self.circle_plane, DOWN, @@ -1235,14 +1245,58 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene): )) self.wait() - def draw_full_frequency_plot(self): graph = self.graph fourier_graph = self.get_fourier_transform_graph(graph) + fourier_graph.save_state() + v_line = DashedLine( + self.frequency_axes.coords_to_point(0, 0), + self.frequency_axes.coords_to_point(0, 1), + stroke_width = 6, + color = fourier_graph.get_color() + ) + dot = Dot(color = fourier_graph.get_color()) + def update_dot(dot): + f = self.graph.polarized_mobject.frequency + dot.move_to(self.frequency_axes.input_to_graph_point( + f, fourier_graph + )) + dot_update_anim = UpdateFromFunc(dot, update_dot) - - self.revert_to_original_skipping_status() self.change_frequency(0.0) + dot_update_anim.update(0) + self.wait() + self.play(ShowCreation(v_line)) + self.play(GrowFromCenter(dot), FadeOut(v_line)) + f_max = int(self.frequency_axes.x_max) + for freq in range(1, f_max+1): + fourier_graph.restore() + self.change_frequency( + freq, + added_anims = [ + ShowCreation( + fourier_graph, + rate_func = lambda t : interpolate( + (freq-1.)/f_max, + float(freq)/f_max, + smooth(t) + ), + ), + dot_update_anim + ], + run_time = 5, + ) + self.wait() + self.play(FadeOut(dot)) + self.fourier_graph = fourier_graph + + def recap_objects_on_screen(self): + rect = FullScreenFadeRectangle() + + self.play(FadeIn(rect)) + + def lower_graph(self): + pass def label_as_almost_fourier(self): pass @@ -1262,20 +1316,19 @@ class DrawFrequencyPlot(WrapCosineGraphAroundCircle, PiCreatureScene): def change_frequency(self, new_freq, **kwargs): kwargs["run_time"] = kwargs.get("run_time", 3) + added_anims = kwargs.get("added_anims", []) freq_label = filter( lambda sm : isinstance(sm, DecimalNumber), self.winding_freq_label )[0] anims = [ - Transform( - self.v_lines_indicating_periods, - self.get_v_lines_indicating_periods(new_freq) - ), ChangeDecimalToValue(freq_label, new_freq), self.get_frequency_change_animation( self.graph, new_freq ), + self.get_period_v_lines_update_anim(), ] + anims += added_anims #TODO, conditionals for center of mass if hasattr(self, "center_of_mass_dot"): anims.append(self.center_of_mass_dot_anim) diff --git a/scene/scene.py b/scene/scene.py index 84ea28c1..5b515472 100644 --- a/scene/scene.py +++ b/scene/scene.py @@ -397,7 +397,6 @@ class Scene(object): if self.skip_animations: kwargs["run_time"] = 0 - animations = self.compile_play_args_to_animation_list(*args) self.num_plays += 1 From 4919dbf652f144cf7ff6599bf8ff30d452b6e716 Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Fri, 19 Jan 2018 13:30:10 -0800 Subject: [PATCH 3/3] Fixed ContinualUpdateFromFunc issue --- animation/continual_animation.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/animation/continual_animation.py b/animation/continual_animation.py index bf8c4eab..3ef1ba83 100644 --- a/animation/continual_animation.py +++ b/animation/continual_animation.py @@ -94,16 +94,23 @@ class AmbientMovement(ContinualAnimation): self.mobject.shift(dt*self.rate*self.direction) class ContinualUpdateFromFunc(ContinualAnimation): + CONFIG = { + "function_depends_on_dt" : False + } def __init__(self, mobject, func, **kwargs): self.func = func - self.func_arg_count = func.func_code.co_argcount - if self.func_arg_count > 2: - raise Exception("ContinualUpdateFromFunc function must take 1 or 2 args") ContinualAnimation.__init__(self, mobject, **kwargs) def update_mobject(self, dt): - args = (self.mobject, dt) - self.func(*args[:self.func_arg_count]) + if self.function_depends_on_dt: + self.func(self.mobject, dt) + else: + self.func(self.mobject) + +class ContinualUpdateFromTimeFunc(ContinualUpdateFromFunc): + CONFIG = { + "function_depends_on_dt" : True + } class ContinualMaintainPositionRelativeTo(ContinualAnimation): # TODO: Possibly reimplement using CycleAnimation?