123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import copy
- import inspect
- import sys
- from manimlib.config import get_custom_config
- from manimlib.logger import log
- from manimlib.scene.interactive_scene import InteractiveScene
- from manimlib.scene.scene import Scene
- class BlankScene(InteractiveScene):
- def construct(self):
- exec(get_custom_config()["universal_import_line"])
- self.embed()
- def is_child_scene(obj, module):
- if not inspect.isclass(obj):
- return False
- if not issubclass(obj, Scene):
- return False
- if obj == Scene:
- return False
- if not obj.__module__.startswith(module.__name__):
- return False
- return True
- def prompt_user_for_choice(scene_classes):
- name_to_class = {}
- max_digits = len(str(len(scene_classes)))
- for idx, scene_class in enumerate(scene_classes, start=1):
- name = scene_class.__name__
- print(f"{str(idx).zfill(max_digits)}: {name}")
- name_to_class[name] = scene_class
- try:
- user_input = input(
- "\nThat module has multiple scenes, " + \
- "which ones would you like to render?" + \
- "\nScene Name or Number: "
- )
- return [
- name_to_class[split_str] if not split_str.isnumeric() else scene_classes[int(split_str) - 1]
- for split_str in user_input.replace(" ", "").split(",")
- ]
- except IndexError:
- log.error("Invalid scene number")
- sys.exit(2)
- except KeyError:
- log.error("Invalid scene name")
- sys.exit(2)
- except EOFError:
- sys.exit(1)
- def get_scene_config(config):
- scene_parameters = inspect.signature(Scene).parameters.keys()
- return {
- key: config[key]
- for key in set(scene_parameters).intersection(config.keys())
- }
- def compute_total_frames(scene_class, scene_config):
- """
- When a scene is being written to file, a copy of the scene is run with
- skip_animations set to true so as to count how many frames it will require.
- This allows for a total progress bar on rendering, and also allows runtime
- errors to be exposed preemptively for long running scenes.
- """
- pre_config = copy.deepcopy(scene_config)
- pre_config["file_writer_config"]["write_to_movie"] = False
- pre_config["file_writer_config"]["save_last_frame"] = False
- pre_config["file_writer_config"]["quiet"] = True
- pre_config["skip_animations"] = True
- pre_scene = scene_class(**pre_config)
- pre_scene.run()
- total_time = pre_scene.time - pre_scene.skip_time
- return int(total_time * scene_config["camera_config"]["fps"])
- def scene_from_class(scene_class, scene_config, config):
- fw_config = scene_config["file_writer_config"]
- if fw_config["write_to_movie"] and config["prerun"]:
- fw_config["total_frames"] = compute_total_frames(scene_class, scene_config)
- return scene_class(**scene_config)
- def get_scenes_to_render(all_scene_classes, scene_config, config):
- if config["write_all"]:
- return [sc(**scene_config) for sc in all_scene_classes]
- names_to_classes = {sc.__name__ : sc for sc in all_scene_classes}
- scene_names = config["scene_names"]
- for name in set.difference(set(scene_names), names_to_classes):
- log.error(f"No scene named {name} found")
- scene_names.remove(name)
- if scene_names:
- classes_to_run = [names_to_classes[name] for name in scene_names]
- elif len(all_scene_classes) == 1:
- classes_to_run = [all_scene_classes[0]]
- else:
- classes_to_run = prompt_user_for_choice(all_scene_classes)
- return [
- scene_from_class(scene_class, scene_config, config)
- for scene_class in classes_to_run
- ]
- def get_scene_classes_from_module(module):
- if hasattr(module, "SCENES_IN_ORDER"):
- return module.SCENES_IN_ORDER
- else:
- return [
- member[1]
- for member in inspect.getmembers(
- module,
- lambda x: is_child_scene(x, module)
- )
- ]
- def main(config):
- module = config["module"]
- scene_config = get_scene_config(config)
- if module is None:
- # If no module was passed in, just play the blank scene
- return [BlankScene(**scene_config)]
- all_scene_classes = get_scene_classes_from_module(module)
- scenes = get_scenes_to_render(all_scene_classes, scene_config, config)
- return scenes
|