diff --git a/active_projects/clacks.py b/active_projects/clacks.py index e9085073..387cb647 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks.py @@ -344,7 +344,7 @@ class BlocksAndWallScene(Scene): self.counter_mob.set_value(n_clacks) def create_sound_file(self, clack_data): - directory = get_scene_output_directory(self.__class__) + directory = get_scene_output_directory(BlocksAndWallScene) clack_file = os.path.join( directory, 'sounds', self.collision_sound, ) @@ -674,9 +674,11 @@ class Mass1e1WithElasticLabel(BlocksAndWallExampleMass1e1): def get_arrow(self, label, clack_flashes, flash): arrow = Arrow( label.get_bottom(), - flash.mobject.get_center() + 0.5 * UP, + flash.mobject.get_center() + 0.0 * UP, ) arrow.set_fill(YELLOW) + arrow.set_stroke(BLACK, 1, background=True) + arrow.original_length = arrow.get_length() def set_opacity(arrow): time = self.get_time() @@ -684,8 +686,14 @@ class Mass1e1WithElasticLabel(BlocksAndWallExampleMass1e1): if from_start < 0: opacity = 0 else: - opacity = smooth(1 - from_start) + opacity = smooth(1 - 2 * from_start) arrow.set_fill(opacity=opacity) + arrow.set_stroke(opacity=opacity, background=True) + # if opacity > 0: + # arrow.scale( + # opacity * arrow.original_length / arrow.get_length(), + # about_point=arrow.get_end() + # ) arrow.add_updater(set_opacity) return arrow @@ -844,10 +852,16 @@ class DigitsOfPi(Scene): equation = TexMobject( "\\pi = 3.14159265..." ) - self.add(equation[:2]) + equation.set_color(YELLOW) + pi_creature = Randolph(color=YELLOW) + pi_creature.match_width(equation[0]) + 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(Blink(pi_creature)) self.wait() @@ -1504,7 +1518,7 @@ class NextVideo(Scene): class EndScreen(Scene): def construct(self): - width = (500 / 1280) * FRAME_WIDTH + width = (475 / 1280) * FRAME_WIDTH height = width * (323 / 575) video_rect = Rectangle( width=width, @@ -1537,3 +1551,48 @@ class EndScreen(Scene): ShowCreation(video_rects[n % 4]), run_time=2, ) + + +class Thumbnail(BlocksAndWallExample): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "mass": 1e4, + "velocity": -1.5, + }, + "collect_clack_data": False, + }, + "wait_time": 0, + "count_clacks": False, + "show_flash_animations": False, + "floor_y": -3, + } + + def construct(self): + self.floor.set_stroke(WHITE, 10) + self.wall.set_stroke(WHITE, 10) + self.wall[1:].set_stroke(WHITE, 4) + 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) + + arrow = Vector( + 2.5 * LEFT, + color=RED, + rectangular_stem_width=1.5, + tip_length=0.5 + ) + arrow.move_to(blocks.block1.get_center(), RIGHT) + arrow.add_to_back( + arrow.copy().set_stroke(GREY, 5) + ) + self.add(arrow) + + 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 diff --git a/active_projects/clacks_solution1.py b/active_projects/clacks_solution1.py new file mode 100644 index 00000000..4484583b --- /dev/null +++ b/active_projects/clacks_solution1.py @@ -0,0 +1,43 @@ +from big_ol_pile_of_manim_imports import * +from active_projects.clacks import * + + + +class LastVideo(Scene): + def construct(self): + pass + + +class BlocksAndWallExampleMass16(BlocksAndWallExample): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "mass": 16, + "velocity": -1.5, + }, + }, + "wait_time": 25, + } + + + +class Mass16WithElasticLabel(Mass1e1WithElasticLabel): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "mass": 16, + } + }, + } + + +class BlocksAndWallExampleMass64(BlocksAndWallExample): + CONFIG = { + "sliding_blocks_config": { + "block1_config": { + "mass": 64, + "velocity": -1.5, + }, + }, + "wait_time": 25, + } \ No newline at end of file diff --git a/manimlib/config.py b/manimlib/config.py index ac150bc1..ce540a72 100644 --- a/manimlib/config.py +++ b/manimlib/config.py @@ -18,8 +18,8 @@ def parse_cli(): help="path to file holding the python code for the scene", ) parser.add_argument( - "scene_name", - nargs="?", + "scene_names", + nargs="+", help="Name of the Scene class you want to see", ) optional_args = [ @@ -36,7 +36,7 @@ def parse_cli(): ] 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("-o", "--output_file_name") parser.add_argument("-n", "--start_at_animation_number") parser.add_argument("-r", "--resolution") parser.add_argument("-c", "--color") @@ -96,23 +96,23 @@ def get_module(file_name): def get_configuration(args): - if args.output_name is not None: - output_name_root, output_name_ext = os.path.splitext( - args.output_name) + if args.output_file_name is not None: + output_file_name_root, output_file_name_ext = os.path.splitext( + args.output_file_name) expected_ext = '.png' if args.show_last_frame else '.mp4' - if output_name_ext not in ['', expected_ext]: + if output_file_name_ext not in ['', expected_ext]: print("WARNING: The output will be to (doubly-dotted) %s%s" % - output_name_root % expected_ext) - output_name = args.output_name + output_file_name_root % expected_ext) + output_file_name = args.output_file_name else: # If anyone wants .mp4.mp4 and is surprised to only get .mp4, or such... Well, too bad. - output_name = output_name_root + output_file_name = output_file_name_root else: - output_name = args.output_name + output_file_name = args.output_file_name config = { "module": get_module(args.file), - "scene_name": args.scene_name, + "scene_names": args.scene_names, "open_video_upon_completion": args.preview, "show_file_in_finder": args.show_file_in_finder, # By default, write to file @@ -125,7 +125,7 @@ def get_configuration(args): "quiet": args.quiet or args.write_all, "ignore_waits": args.preview, "write_all": args.write_all, - "output_name": output_name, + "output_file_name": output_file_name, "start_at_animation_number": args.start_at_animation_number, "end_at_animation_number": None, "sound": args.sound, diff --git a/manimlib/constants.py b/manimlib/constants.py index 99327ff4..8dcc14d1 100644 --- a/manimlib/constants.py +++ b/manimlib/constants.py @@ -69,7 +69,7 @@ HELP_MESSAGE = """ -c specify a background color """ SCENE_NOT_FOUND_MESSAGE = """ - That scene is not in the script + {} is not in the script """ CHOOSE_NUMBER_MESSAGE = """ Choose number corresponding to desired scene/arguments. diff --git a/manimlib/extract_scene.py b/manimlib/extract_scene.py index 6885d869..2e2348b3 100644 --- a/manimlib/extract_scene.py +++ b/manimlib/extract_scene.py @@ -98,20 +98,29 @@ def get_scene_classes(scene_names_to_classes, config): if len(scene_names_to_classes) == 0: print(manimlib.constants.NO_SCENE_MESSAGE) return [] - if config["scene_name"] in scene_names_to_classes: - return [scene_names_to_classes[config["scene_name"]]] - if config["scene_name"] != "": - print(manimlib.constants.SCENE_NOT_FOUND_MESSAGE, file=sys.stderr) - sys.exit(2) if config["write_all"]: return list(scene_names_to_classes.values()) + scene_classes = [] + for scene_name in config["scene_names"]: + if scene_name in scene_names_to_classes: + scene_classes.append(scene_names_to_classes[scene_name]) + elif scene_name != "": + print( + manimlib.constants.SCENE_NOT_FOUND_MESSAGE.format( + scene_name + ), + file=sys.stderr + ) + if scene_classes: + return scene_classes return prompt_user_for_choice(scene_names_to_classes) def main(config): module = config["module"] scene_names_to_classes = dict( - inspect.getmembers(module, lambda x: is_child_scene(x, module))) + inspect.getmembers(module, lambda x: is_child_scene(x, module)) + ) scene_kwargs = dict([ (key, config[key]) @@ -124,10 +133,9 @@ def main(config): "movie_file_extension", "start_at_animation_number", "end_at_animation_number", + "output_file_name" ] ]) - - scene_kwargs["name"] = config["output_name"] if config["save_pngs"]: print("We are going to save a PNG sequence as well...") scene_kwargs["save_pngs"] = True @@ -138,14 +146,12 @@ def main(config): handle_scene(SceneClass(**scene_kwargs), **config) if config["sound"]: play_finish_sound() - sys.exit(0) except Exception: print("\n\n") traceback.print_exc() print("\n\n") if config["sound"]: play_error_sound() - sys.exit(2) if __name__ == "__main__": diff --git a/manimlib/scene/scene.py b/manimlib/scene/scene.py index ab60ce77..b4331f79 100644 --- a/manimlib/scene/scene.py +++ b/manimlib/scene/scene.py @@ -47,6 +47,7 @@ class Scene(Container): "livestreaming": False, "to_twitch": False, "twitch_key": None, + "output_file_name": None, } def __init__(self, **kwargs): @@ -111,6 +112,11 @@ class Scene(Container): def __str__(self): return self.__class__.__name__ + def get_output_file_name(self): + if self.output_file_name is not None: + return self.output_file_name + return str(self) + def set_variables_as_attrs(self, *objects, **newly_named_objects): """ This method is slightly hacky, making it a little easier @@ -597,10 +603,13 @@ class Scene(Container): 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 = str(self) + sub_dir = output_file_name path = get_image_output_directory(self.__class__, sub_dir) - file_name = add_extension_if_not_present(name or str(self), ".png") + 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): @@ -618,7 +627,7 @@ class Scene(Container): if extension is None: extension = self.movie_file_extension if name is None: - name = str(self) + name = self.get_output_file_name() file_path = os.path.join(directory, name) if not file_path.endswith(extension): file_path += extension