mirror of
https://github.com/3b1b/manim.git
synced 2025-04-13 09:47:07 +00:00
commit
c06f7652cf
8 changed files with 478 additions and 62 deletions
|
@ -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,
|
||||
)
|
||||
|
@ -376,8 +376,8 @@ class BlocksAndWallScene(Scene):
|
|||
clacks.export(output_file, format="wav")
|
||||
return output_file
|
||||
|
||||
def close_movie_pipe(self):
|
||||
Scene.close_movie_pipe(self)
|
||||
def combine_movie_files(self):
|
||||
Scene.combine_movie_files(self)
|
||||
if self.include_sound:
|
||||
sound_file_path = self.create_sound_file(self.clack_data)
|
||||
movie_path = self.get_movie_file_path()
|
||||
|
@ -398,6 +398,42 @@ class BlocksAndWallScene(Scene):
|
|||
# Animated scenes
|
||||
|
||||
|
||||
class NameIntro(Scene):
|
||||
def construct(self):
|
||||
name = TextMobject("3Blue", "1Brown", arg_separator="")
|
||||
blue, brown = name
|
||||
name.scale(2.5)
|
||||
for part in name:
|
||||
part.save_state()
|
||||
brown.to_edge(RIGHT, buff=0)
|
||||
flash_time = 0.75
|
||||
|
||||
self.add(blue, brown)
|
||||
self.play(
|
||||
VFadeIn(blue),
|
||||
VFadeIn(brown),
|
||||
Restore(brown, rate_func=None),
|
||||
)
|
||||
self.play(
|
||||
Flash(blue.get_right(), run_time=flash_time),
|
||||
ApplyMethod(
|
||||
blue.to_edge, LEFT, {"buff": 0},
|
||||
rate_func=None,
|
||||
),
|
||||
)
|
||||
self.play(
|
||||
Flash(blue.get_left(), run_time=flash_time),
|
||||
Restore(blue, rate_func=None),
|
||||
)
|
||||
self.play(
|
||||
Flash(blue.get_right(), run_time=flash_time),
|
||||
ApplyMethod(
|
||||
brown.to_edge, RIGHT, {"buff": 0},
|
||||
rate_func=None,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class MathAndPhysicsConspiring(Scene):
|
||||
def construct(self):
|
||||
v_line = Line(DOWN, UP).scale(FRAME_HEIGHT)
|
||||
|
@ -547,11 +583,12 @@ class BlocksAndWallExample(BlocksAndWallScene):
|
|||
CONFIG = {
|
||||
"sliding_blocks_config": {
|
||||
"block1_config": {
|
||||
"mass": 1e0,
|
||||
# "mass": 1e0,
|
||||
"mass": 64,
|
||||
"velocity": -2,
|
||||
}
|
||||
},
|
||||
"wait_time": 10,
|
||||
"wait_time": 15,
|
||||
}
|
||||
|
||||
def construct(self):
|
||||
|
@ -570,6 +607,37 @@ class BlocksAndWallExampleMass1e1(BlocksAndWallExample):
|
|||
}
|
||||
|
||||
|
||||
class TwoBlocksLabel(Scene):
|
||||
def construct(self):
|
||||
label = TextMobject("Two sliding \\\\ blocks")
|
||||
label.to_edge(UP)
|
||||
arrows = VGroup(*[
|
||||
Arrow(label.get_bottom(), point)
|
||||
for point in [RIGHT, LEFT]
|
||||
])
|
||||
arrows.set_color(RED)
|
||||
self.play(
|
||||
Write(label),
|
||||
LaggedStart(GrowArrow, arrows, lag_ratio=0.7),
|
||||
run_time=1
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class WallLabel(Scene):
|
||||
def construct(self):
|
||||
wall = Line(TOP, 2 * DOWN)
|
||||
wall.set_stroke(YELLOW, 10)
|
||||
word = TextMobject("Wall")
|
||||
word.rotate(-90 * DEGREES)
|
||||
word.next_to(wall, RIGHT, MED_SMALL_BUFF)
|
||||
self.play(
|
||||
Write(word),
|
||||
ShowPassingFlash(wall)
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class CowToSphere(ExternallyAnimatedScene):
|
||||
pass
|
||||
|
||||
|
@ -606,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()
|
||||
|
@ -616,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
|
||||
|
@ -626,14 +702,13 @@ class Mass1e1WithElasticLabel(BlocksAndWallExampleMass1e1):
|
|||
class AskAboutSoundlessness(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
self.student_says(
|
||||
"Wait, elastic collisions should\\\\"
|
||||
"make no sound, right?",
|
||||
"No sound,\\\\right?"
|
||||
)
|
||||
self.play(self.teacher.change, "guilty")
|
||||
self.wait(2)
|
||||
self.play(
|
||||
RemovePiCreatureBubble(self.students[1], target_mode="confused"),
|
||||
self.teacher.change, "raise_right_hand",
|
||||
self.teacher_says(
|
||||
"Focus on \\\\ collisions",
|
||||
target_mode="speaking",
|
||||
added_anims=[
|
||||
self.get_student_changes("pondering", "confused", "thinking")
|
||||
]
|
||||
|
@ -681,10 +756,10 @@ class BlocksAndWallExampleMass1e2(BlocksAndWallExample):
|
|||
"sliding_blocks_config": {
|
||||
"block1_config": {
|
||||
"mass": 1e2,
|
||||
"velocity": -1,
|
||||
"velocity": -0.6,
|
||||
}
|
||||
},
|
||||
"wait_time": 20,
|
||||
"wait_time": 25,
|
||||
}
|
||||
|
||||
|
||||
|
@ -772,6 +847,24 @@ class BlocksAndWallExampleMass1e10(BlocksAndWallExample):
|
|||
}
|
||||
|
||||
|
||||
class DigitsOfPi(Scene):
|
||||
def construct(self):
|
||||
equation = TexMobject(
|
||||
"\\pi = 3.14159265..."
|
||||
)
|
||||
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()
|
||||
|
||||
|
||||
class GalperinPaperScroll(ExternallyAnimatedScene):
|
||||
pass
|
||||
|
||||
|
@ -902,19 +995,7 @@ class PiComputingAlgorithmsAxes(Scene):
|
|||
|
||||
class StepsOfTheAlgorithm(TeacherStudentsScene):
|
||||
def construct(self):
|
||||
steps = VGroup(
|
||||
TextMobject("Step 1:", "Implement a physics engine"),
|
||||
TextMobject(
|
||||
"Step 2:",
|
||||
"Choose the number of digits, $d$,\\\\"
|
||||
"of $\\pi$ that you want to compute"
|
||||
),
|
||||
TextMobject(
|
||||
"Step 3:",
|
||||
"Set one mass to $100^{d - 1}$, the other to $1$"
|
||||
),
|
||||
TextMobject("Step 4:", "Count collisions"),
|
||||
)
|
||||
steps = self.get_steps()
|
||||
steps.arrange_submobjects(
|
||||
DOWN,
|
||||
buff=MED_LARGE_BUFF,
|
||||
|
@ -943,6 +1024,59 @@ class StepsOfTheAlgorithm(TeacherStudentsScene):
|
|||
)
|
||||
self.wait(3)
|
||||
|
||||
def get_steps(self):
|
||||
return VGroup(
|
||||
TextMobject("Step 1:", "Implement a physics engine"),
|
||||
TextMobject(
|
||||
"Step 2:",
|
||||
"Choose the number of digits, $d$,\\\\"
|
||||
"of $\\pi$ that you want to compute"
|
||||
),
|
||||
TextMobject(
|
||||
"Step 3:",
|
||||
"Set one mass to $100^{d - 1}$,\\\\"
|
||||
"the other to $1$"
|
||||
),
|
||||
TextMobject("Step 4:", "Count collisions"),
|
||||
)
|
||||
|
||||
|
||||
class StepsOfTheAlgorithmJustTitles(StepsOfTheAlgorithm):
|
||||
def construct(self):
|
||||
self.remove(*self.pi_creatures)
|
||||
titles = self.get_steps()
|
||||
for title in titles:
|
||||
title.scale(1.5)
|
||||
title.to_edge(UP)
|
||||
|
||||
last_title = VectorizedPoint()
|
||||
for title in titles:
|
||||
self.play(
|
||||
FadeInFromDown(title),
|
||||
FadeOutAndShift(last_title, UP),
|
||||
)
|
||||
self.wait()
|
||||
last_title = title
|
||||
|
||||
|
||||
class BlocksAndWallExampleToShowWithSteps(BlocksAndWallExample):
|
||||
CONFIG = {
|
||||
"sliding_blocks_config": {
|
||||
"block1_config": {
|
||||
"mass": 1e22,
|
||||
"velocity": -1,
|
||||
"label_text": "$100^{(12 - 1)}$\\,kg",
|
||||
"width": 2,
|
||||
},
|
||||
"collect_clack_data": False,
|
||||
},
|
||||
"wait_time": 25,
|
||||
"counter_group_shift_vect": 5 * LEFT,
|
||||
"count_clacks": True,
|
||||
"include_sound": False,
|
||||
"show_flash_animations": False,
|
||||
}
|
||||
|
||||
|
||||
class CompareToGalacticMass(Scene):
|
||||
def construct(self):
|
||||
|
@ -1174,6 +1308,22 @@ class BlocksAndWallExampleGalacticMass(BlocksAndWallExample):
|
|||
self.add(words)
|
||||
|
||||
|
||||
class RealPhysicsVsThis(Scene):
|
||||
def construct(self):
|
||||
physics = TextMobject("Real physics")
|
||||
this = TextMobject("This process")
|
||||
this.set_color()
|
||||
physics.to_edge(LEFT)
|
||||
this.next_to(physics)
|
||||
self.add(physics, this)
|
||||
self.play(
|
||||
this.shift, FRAME_WIDTH * RIGHT,
|
||||
rate_func=rush_into,
|
||||
run_time=3,
|
||||
)
|
||||
self.wait()
|
||||
|
||||
|
||||
class CompareAlgorithmToPhysics(PiCreatureScene):
|
||||
def construct(self):
|
||||
morty = self.pi_creature
|
||||
|
@ -1284,4 +1434,165 @@ class LightBouncingFanning(LightBouncingNoFanning):
|
|||
|
||||
class NextVideo(Scene):
|
||||
def construct(self):
|
||||
pass
|
||||
videos = VGroup(*[VideoIcon() for x in range(2)])
|
||||
videos.set_height(2)
|
||||
for video in videos:
|
||||
video.set_color(BLUE)
|
||||
video.set_sheen(0.5, UL)
|
||||
videos.arrange_submobjects(RIGHT, buff=2)
|
||||
|
||||
titles = VGroup(
|
||||
TextMobject("Here and now"),
|
||||
TextMobject("Solution"),
|
||||
)
|
||||
for title, video in zip(titles, videos):
|
||||
# title.scale(1.5)
|
||||
title.next_to(video, UP)
|
||||
video.add(title)
|
||||
|
||||
dots = TextMobject(".....")
|
||||
dots.scale(2)
|
||||
dots.move_to(videos)
|
||||
|
||||
mid_words = TextMobject(
|
||||
"Patient\\\\", "problem\\\\", "solving"
|
||||
)
|
||||
mid_words.next_to(dots, DOWN)
|
||||
randy = Randolph(height=1)
|
||||
randy.next_to(dots, UP, SMALL_BUFF)
|
||||
thought_bubble = ThoughtBubble(height=2, width=2, direction=LEFT)
|
||||
thought_bubble.set_stroke(width=2)
|
||||
thought_bubble.move_to(randy.get_corner(UR), DL)
|
||||
speech_bubble = SpeechBubble(height=2, width=2)
|
||||
speech_bubble.pin_to(randy)
|
||||
speech_bubble.write("What do \\\\ you think?")
|
||||
friends = VGroup(
|
||||
PiCreature(color=BLUE_E),
|
||||
PiCreature(color=BLUE_C),
|
||||
Mortimer()
|
||||
)
|
||||
friends.set_height(1)
|
||||
friends.arrange_submobjects(RIGHT, buff=MED_SMALL_BUFF)
|
||||
friends[:2].next_to(randy, LEFT)
|
||||
friends[2].next_to(randy, RIGHT)
|
||||
|
||||
self.add(videos[0])
|
||||
self.wait()
|
||||
self.play(
|
||||
TransformFromCopy(*videos),
|
||||
)
|
||||
self.play(Write(dots))
|
||||
self.wait()
|
||||
self.play(
|
||||
LaggedStart(
|
||||
FadeInFrom, mid_words,
|
||||
lambda m: (m, UP),
|
||||
lag_ratio=0.8,
|
||||
),
|
||||
randy.change, "pondering",
|
||||
VFadeIn(randy),
|
||||
videos.space_out_submobjects, 1.3,
|
||||
)
|
||||
self.play(ShowCreation(thought_bubble))
|
||||
self.play(Blink(randy))
|
||||
self.play(
|
||||
Uncreate(thought_bubble),
|
||||
ShowCreation(speech_bubble),
|
||||
Write(speech_bubble.content),
|
||||
randy.change, "maybe", friends[0].eyes,
|
||||
LaggedStart(FadeInFromDown, friends),
|
||||
videos.space_out_submobjects, 1.6,
|
||||
)
|
||||
self.play(
|
||||
LaggedStart(
|
||||
ApplyMethod, friends,
|
||||
lambda m: (m.change, "pondering"),
|
||||
run_time=1,
|
||||
lag_ratio=0.7,
|
||||
)
|
||||
)
|
||||
self.play(Blink(friends[2]))
|
||||
self.play(friends[0].change, "confused")
|
||||
self.wait()
|
||||
|
||||
|
||||
class EndScreen(Scene):
|
||||
def construct(self):
|
||||
width = (475 / 1280) * FRAME_WIDTH
|
||||
height = width * (323 / 575)
|
||||
video_rect = Rectangle(
|
||||
width=width,
|
||||
height=height,
|
||||
)
|
||||
video_rect.shift(UP)
|
||||
video_rects = VGroup(*[
|
||||
video_rect.copy().set_color(color)
|
||||
for color in [BLUE_E, BLUE_C, BLUE_D, GREY_BROWN]
|
||||
])
|
||||
for rect in video_rects[1::2]:
|
||||
rect.reverse_points()
|
||||
video_rect.set_fill(DARK_GREY, 0.5)
|
||||
video_rect.set_stroke(GREY_BROWN, 0.5)
|
||||
date = TextMobject(
|
||||
"Solution will be\\\\"
|
||||
"posted", "1/20/19",
|
||||
)
|
||||
date[1].set_color(YELLOW)
|
||||
date.set_width(video_rect.get_width() - 2 * MED_SMALL_BUFF)
|
||||
date.move_to(video_rect)
|
||||
|
||||
handle = TextMobject("@3blue1brown")
|
||||
handle.next_to(video_rect, DOWN, MED_LARGE_BUFF)
|
||||
|
||||
self.add(video_rect, date, handle)
|
||||
for n in range(10):
|
||||
self.play(
|
||||
FadeOut(video_rects[(n - 1) % 4]),
|
||||
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)
|
43
active_projects/clacks_solution1.py
Normal file
43
active_projects/clacks_solution1.py
Normal 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,
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -25,8 +25,6 @@ with open("media_dir.txt", 'w') as media_file:
|
|||
VIDEO_DIR = os.path.join(MEDIA_DIR, "videos")
|
||||
RASTER_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "raster_images")
|
||||
SVG_IMAGE_DIR = os.path.join(MEDIA_DIR, "designs", "svg_images")
|
||||
# TODO, staged scenes should really go into a subdirectory of a given scenes directory
|
||||
STAGED_SCENES_DIR = os.path.join(VIDEO_DIR, "staged_scenes")
|
||||
###
|
||||
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
FILE_DIR = os.path.join(THIS_DIR, "files")
|
||||
|
@ -35,8 +33,8 @@ TEX_DIR = os.path.join(FILE_DIR, "Tex")
|
|||
MOBJECT_DIR = os.path.join(FILE_DIR, "mobjects")
|
||||
IMAGE_MOBJECT_DIR = os.path.join(MOBJECT_DIR, "image")
|
||||
|
||||
for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, VIDEO_DIR, TEX_DIR,
|
||||
MOBJECT_DIR, IMAGE_MOBJECT_DIR, STAGED_SCENES_DIR]:
|
||||
for folder in [FILE_DIR, RASTER_IMAGE_DIR, SVG_IMAGE_DIR, VIDEO_DIR,
|
||||
TEX_DIR, MOBJECT_DIR, IMAGE_MOBJECT_DIR]:
|
||||
if not os.path.exists(folder):
|
||||
os.makedirs(folder)
|
||||
|
||||
|
@ -71,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.
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -769,10 +769,55 @@ class Logo(VMobject):
|
|||
fill_color=BLACK,
|
||||
fill_opacity=1,
|
||||
stroke_width=0,
|
||||
sheen=0.0
|
||||
sheen=0.0,
|
||||
start_angle=90 * DEGREES,
|
||||
)
|
||||
self.add(self.pupil)
|
||||
|
||||
def cut_pupil(self):
|
||||
pupil = self.pupil
|
||||
center = pupil.get_center()
|
||||
new_pupil = VGroup(*[
|
||||
pupil.copy().pointwise_become_partial(pupil, a, b)
|
||||
for (a, b) in [(0.25, 1), (0, 0.25)]
|
||||
])
|
||||
for sector in new_pupil:
|
||||
sector.add_control_points([
|
||||
sector.points[-1],
|
||||
*[center] * 3,
|
||||
*[sector.points[0]] * 2
|
||||
])
|
||||
self.remove(pupil)
|
||||
self.add(new_pupil)
|
||||
self.pupil = new_pupil
|
||||
|
||||
def get_blue_part_and_brown_part(self):
|
||||
if len(self.pupil) == 1:
|
||||
self.cut_pupil()
|
||||
# circle = Circle()
|
||||
# circle.set_stroke(width=0)
|
||||
# circle.set_fill(BLACK, opacity=1)
|
||||
# circle.match_width(self)
|
||||
# circle.move_to(self)
|
||||
blue_part = VGroup(
|
||||
self.iris_background[0],
|
||||
*[
|
||||
layer[:layer.brown_index]
|
||||
for layer in self.spike_layers
|
||||
],
|
||||
self.pupil[0],
|
||||
)
|
||||
brown_part = VGroup(
|
||||
self.iris_background[1],
|
||||
*[
|
||||
layer[layer.brown_index:]
|
||||
for layer in self.spike_layers
|
||||
],
|
||||
self.pupil[1],
|
||||
)
|
||||
return blue_part, brown_part
|
||||
|
||||
|
||||
|
||||
# Cards
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -37,7 +37,11 @@ def get_movie_output_directory(scene_class, camera_config, frame_duration):
|
|||
def get_partial_movie_output_directory(scene_class, camera_config, frame_duration):
|
||||
directory = get_movie_output_directory(scene_class, camera_config, frame_duration)
|
||||
return guarantee_existance(
|
||||
os.path.join(directory, scene_class.__name__)
|
||||
os.path.join(
|
||||
directory,
|
||||
"partial_movie_files",
|
||||
scene_class.__name__
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
@ -74,4 +78,4 @@ def get_sorted_integer_files(directory,
|
|||
elif remove_non_integer_files:
|
||||
os.remove(full_path)
|
||||
indexed_files.sort(key=lambda p: p[0])
|
||||
return map(lambda p: p[1], indexed_files)
|
||||
return list(map(lambda p: p[1], indexed_files))
|
||||
|
|
Loading…
Add table
Reference in a new issue