Make it possible to render multiple scenes

This commit is contained in:
Grant Sanderson 2019-01-12 12:31:29 -08:00
parent 4ae38cd9b8
commit 199a3b82c6
6 changed files with 149 additions and 32 deletions

View file

@ -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)

View file

@ -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,
}

View file

@ -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,

View file

@ -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.

View file

@ -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__":

View file

@ -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