Skip to content

Commit 744e695

Browse files
authored
Misc. clean up (#2269)
* Comment tweak * Directly print traceback Since the shell.showtraceback is giving some issues * Make InteracrtiveSceneEmbed into a class This way it can keep track of it's internal shell; use of get_ipython has a finicky relationship with reloading. * Move remaining checkpoint_paste logic into scene_embed.py This involved making a few context managers for Scene: temp_record, temp_skip, temp_progress_bar, which seem useful in and of themselves. * Change null key to be the empty string * Ensure temporary svg paths for Text are deleted * Remove unused dict_ops.py functions * Remove break_into_partial_movies from file_writer configuration * Rewrite guarantee_existence using Path * Clean up SceneFileWriter It had a number of vestigial functions no longer used, and some setup that could be made more organized. * Remove --save_pngs CLI arg (which did nothing) * Add --subdivide CLI arg * Remove add_extension_if_not_present * Remove get_sorted_integer_files * Have find_file return Path * Minor clean up * Clean up num_tex_symbols * Fix find_file * Minor cleanup for extract_scene.py * Add preview_frame_while_skipping option to scene config * Use shell.showtraceback function * Move keybindings to config, instead of in-place constants * Replace DEGREES -> DEG
1 parent 00b34f2 commit 744e695

24 files changed

+209
-353
lines changed

docs/source/documentation/constants.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Mathematical constant
7474
7575
PI = np.pi
7676
TAU = 2 * PI
77-
DEGREES = TAU / 360
77+
DEG = TAU / 360
7878
7979
Text
8080
----

docs/source/getting_started/example_scenes.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ InteractiveDevlopment
3434
self.play(ReplacementTransform(square, circle))
3535
self.wait()
3636
self.play(circle.animate.stretch(4, 0))
37-
self.play(Rotate(circle, 90 * DEGREES))
37+
self.play(Rotate(circle, 90 * DEG))
3838
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
3939

4040
text = Text("""
@@ -221,7 +221,7 @@ TexTransformExample
221221
self.play(
222222
TransformMatchingTex(
223223
lines[0].copy(), lines[1],
224-
path_arc=90 * DEGREES,
224+
path_arc=90 * DEG,
225225
),
226226
**play_kw
227227
)
@@ -599,8 +599,8 @@ SurfaceExample
599599
# Set perspective
600600
frame = self.camera.frame
601601
frame.set_euler_angles(
602-
theta=-30 * DEGREES,
603-
phi=70 * DEGREES,
602+
theta=-30 * DEG,
603+
phi=70 * DEG,
604604
)
605605

606606
surface = surfaces[0]
@@ -624,8 +624,8 @@ SurfaceExample
624624
self.play(
625625
Transform(surface, surfaces[2]),
626626
# Move camera frame during the transition
627-
frame.animate.increment_phi(-10 * DEGREES),
628-
frame.animate.increment_theta(-20 * DEGREES),
627+
frame.animate.increment_phi(-10 * DEG),
628+
frame.animate.increment_theta(-20 * DEG),
629629
run_time=3
630630
)
631631
# Add ambient rotation

example_scenes.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ def construct(self):
190190
# to go to a non-equal substring from the target,
191191
# use the key map.
192192
key_map={"+": "-"},
193-
path_arc=90 * DEGREES,
193+
path_arc=90 * DEG,
194194
),
195195
)
196196
self.wait()
@@ -203,7 +203,7 @@ def construct(self):
203203
TransformMatchingStrings(
204204
lines[2].copy(), lines[3],
205205
key_map={"2": R"\sqrt"},
206-
path_arc=-30 * DEGREES,
206+
path_arc=-30 * DEG,
207207
),
208208
)
209209
self.wait(2)
@@ -616,8 +616,8 @@ def construct(self):
616616
self.play(
617617
Transform(surface, surfaces[2]),
618618
# Move camera frame during the transition
619-
self.frame.animate.increment_phi(-10 * DEGREES),
620-
self.frame.animate.increment_theta(-20 * DEGREES),
619+
self.frame.animate.increment_phi(-10 * DEG),
620+
self.frame.animate.increment_theta(-20 * DEG),
621621
run_time=3
622622
)
623623
# Add ambient rotation
@@ -666,7 +666,7 @@ def construct(self):
666666
self.play(ReplacementTransform(square, circle))
667667
self.wait()
668668
self.play(circle.animate.stretch(4, 0))
669-
self.play(Rotate(circle, 90 * DEGREES))
669+
self.play(Rotate(circle, 90 * DEG))
670670
self.play(circle.animate.shift(2 * RIGHT).scale(0.25))
671671

672672
text = Text("""

manimlib/animation/indication.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from manimlib.constants import FRAME_X_RADIUS, FRAME_Y_RADIUS
1515
from manimlib.constants import ORIGIN, RIGHT, UP
1616
from manimlib.constants import SMALL_BUFF
17-
from manimlib.constants import DEGREES
17+
from manimlib.constants import DEG
1818
from manimlib.constants import TAU
1919
from manimlib.constants import GREY, YELLOW
2020
from manimlib.mobject.geometry import Circle
@@ -395,7 +395,7 @@ def interpolate_submobject(
395395

396396

397397
class TurnInsideOut(Transform):
398-
def __init__(self, mobject: Mobject, path_arc: float = 90 * DEGREES, **kwargs):
398+
def __init__(self, mobject: Mobject, path_arc: float = 90 * DEG, **kwargs):
399399
super().__init__(mobject, path_arc=path_arc, **kwargs)
400400

401401
def create_target(self) -> Mobject:

manimlib/animation/transform.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import numpy as np
66

77
from manimlib.animation.animation import Animation
8-
from manimlib.constants import DEGREES
8+
from manimlib.constants import DEG
99
from manimlib.constants import OUT
1010
from manimlib.mobject.mobject import Group
1111
from manimlib.mobject.mobject import Mobject
@@ -314,7 +314,7 @@ def init_path_func(self) -> None:
314314

315315

316316
class CyclicReplace(Transform):
317-
def __init__(self, *mobjects: Mobject, path_arc=90 * DEGREES, **kwargs):
317+
def __init__(self, *mobjects: Mobject, path_arc=90 * DEG, **kwargs):
318318
super().__init__(Group(*mobjects), path_arc=path_arc, **kwargs)
319319

320320
def create_target(self) -> Mobject:

manimlib/camera/camera_frame.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import numpy as np
77
from scipy.spatial.transform import Rotation
88

9-
from manimlib.constants import DEGREES, RADIANS
9+
from manimlib.constants import DEG, RADIANS
1010
from manimlib.constants import FRAME_SHAPE
1111
from manimlib.constants import DOWN, LEFT, ORIGIN, OUT, RIGHT, UP
1212
from manimlib.constants import PI
@@ -26,7 +26,7 @@ def __init__(
2626
frame_shape: tuple[float, float] = FRAME_SHAPE,
2727
center_point: Vect3 = ORIGIN,
2828
# Field of view in the y direction
29-
fovy: float = 45 * DEGREES,
29+
fovy: float = 45 * DEG,
3030
euler_axes: str = "zxz",
3131
# This keeps it ordered first in a scene
3232
z_index=-1,
@@ -181,7 +181,7 @@ def reorient(
181181
Shortcut for set_euler_angles, defaulting to taking
182182
in angles in degrees
183183
"""
184-
self.set_euler_angles(theta_degrees, phi_degrees, gamma_degrees, units=DEGREES)
184+
self.set_euler_angles(theta_degrees, phi_degrees, gamma_degrees, units=DEG)
185185
if center is not None:
186186
self.move_to(np.array(center))
187187
if height is not None:
@@ -209,7 +209,7 @@ def increment_gamma(self, dgamma: float, units=RADIANS):
209209
self.increment_euler_angles(dgamma=dgamma, units=units)
210210
return self
211211

212-
def add_ambient_rotation(self, angular_speed=1 * DEGREES):
212+
def add_ambient_rotation(self, angular_speed=1 * DEG):
213213
self.add_updater(lambda m, dt: m.increment_theta(angular_speed * dt))
214214
return self
215215

manimlib/config.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,6 @@ def parse_cli():
105105
help="Scene will stay paused during wait calls until " + \
106106
"space bar or right arrow is hit, like a slide show"
107107
)
108-
parser.add_argument(
109-
"-g", "--save_pngs",
110-
action="store_true",
111-
help="Save each frame as a png",
112-
)
113108
parser.add_argument(
114109
"-i", "--gif",
115110
action="store_true",
@@ -148,6 +143,12 @@ def parse_cli():
148143
action="store_true",
149144
help="Show the output file in finder",
150145
)
146+
parser.add_argument(
147+
"--subdivide",
148+
action="store_true",
149+
help="Divide the output animation into individual movie files " +
150+
"for each animation",
151+
)
151152
parser.add_argument(
152153
"--file_name",
153154
help="Name for the movie or image file",
@@ -261,8 +262,8 @@ def update_file_writer_config(config: dict, args: Namespace):
261262
file_writer_config = config["file_writer"]
262263
file_writer_config.update(
263264
write_to_movie=(not args.skip_animations and args.write_file),
265+
subdivide_output=args.subdivide,
264266
save_last_frame=(args.skip_animations and args.write_file),
265-
save_pngs=args.save_pngs,
266267
png_mode=("RGBA" if args.transparent else "RGB"),
267268
movie_file_extension=(get_file_ext(args)),
268269
output_directory=get_output_directory(args, config),

manimlib/constants.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,10 @@
6161
# Angles
6262
PI: float = np.pi
6363
TAU: float = 2 * PI
64-
DEGREES: float = TAU / 360
64+
DEG: float = TAU / 360
65+
DEGREES = DEG # Many older animations use teh full name
6566
# Nice to have a constant for readability
66-
# when juxtaposed with expressions like 30 * DEGREES
67+
# when juxtaposed with expressions like 30 * DEG
6768
RADIANS: float = 1
6869

6970
# Related to Text

manimlib/default_config.yml

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,6 @@ camera:
5252
fps: 30
5353
background_opacity: 1.0
5454
file_writer:
55-
# If break_into_partial_movies is set to True, then many small
56-
# files will be written corresponding to each Scene.play and
57-
# Scene.wait call, and these files will then be combined
58-
# to form the full scene. Sometimes video-editing is made
59-
# easier when working with the broken up scene, which
60-
# effectively has cuts at all the places you might want.
61-
break_into_partial_movies: False
6255
# What command to use for ffmpeg
6356
ffmpeg_bin: "ffmpeg"
6457
# Parameters to pass into ffmpeg
@@ -71,6 +64,9 @@ file_writer:
7164
scene:
7265
show_animation_progress: False
7366
leave_progress_bars: False
67+
# When skipping animations, should a single frame be rendered
68+
# at the end of each play call?
69+
preview_while_skipping: True
7470
# How long does a scene pause on Scene.wait calls
7571
default_wait_time: 1.0
7672
vmobject:
@@ -104,6 +100,20 @@ sizes:
104100
# Default buffers used in Mobject.next_to or Mobject.to_edge
105101
default_mobject_to_edge_buff: 0.5
106102
default_mobject_to_mobject_buff: 0.25
103+
key_bindings:
104+
pan_3d: 'd'
105+
pan: 'f'
106+
reset: 'r'
107+
quit: 'q' # Together with command
108+
select: 's'
109+
unselect: 'u'
110+
grab: 'g'
111+
x_grab: 'h'
112+
y_grab: 'v'
113+
resize: 't'
114+
color: 'c'
115+
information: 'i'
116+
cursor: 'k'
107117
colors:
108118
blue_e: "#1C758A"
109119
blue_d: "#29ABCA"

manimlib/extract_scene.py

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
if TYPE_CHECKING:
1616
Module = importlib.util.types.ModuleType
1717
from typing import Optional
18+
from addict import Dict
1819

1920

2021
class BlankScene(InteractiveScene):
@@ -43,11 +44,7 @@ def prompt_user_for_choice(scene_classes):
4344
print(f"{str(idx).zfill(max_digits)}: {name}")
4445
name_to_class[name] = scene_class
4546
try:
46-
user_input = input(
47-
"\nThat module has multiple scenes, " + \
48-
"which ones would you like to render?" + \
49-
"\nScene Name or Number: "
50-
)
47+
user_input = input("\nSelect which scene to render (by name or number): ")
5148
return [
5249
name_to_class[split_str] if not split_str.isnumeric() else scene_classes[int(split_str) - 1]
5350
for split_str in user_input.replace(" ", "").split(",")
@@ -80,29 +77,29 @@ def compute_total_frames(scene_class, scene_config):
8077
return int(total_time * manim_config.camera.fps)
8178

8279

83-
def scene_from_class(scene_class, scene_config, run_config):
80+
def scene_from_class(scene_class, scene_config: Dict, run_config: Dict):
8481
fw_config = manim_config.file_writer
8582
if fw_config.write_to_movie and run_config.prerun:
8683
scene_config.file_writer_config.total_frames = compute_total_frames(scene_class, scene_config)
8784
return scene_class(**scene_config)
8885

8986

90-
def get_scenes_to_render(all_scene_classes, scene_config, run_config):
91-
if run_config["write_all"]:
92-
return [sc(**scene_config) for sc in all_scene_classes]
87+
def note_missing_scenes(arg_names, module_names):
88+
for name in arg_names:
89+
if name not in module_names:
90+
log.error(f"No scene named {name} found")
9391

94-
names_to_classes = {sc.__name__: sc for sc in all_scene_classes}
95-
scene_names = run_config["scene_names"]
9692

97-
for name in set.difference(set(scene_names), names_to_classes):
98-
log.error(f"No scene named {name} found")
99-
scene_names.remove(name)
100-
101-
if scene_names:
102-
classes_to_run = [names_to_classes[name] for name in scene_names]
103-
elif len(all_scene_classes) == 1:
104-
classes_to_run = [all_scene_classes[0]]
93+
def get_scenes_to_render(all_scene_classes: list, scene_config: Dict, run_config: Dict):
94+
if run_config["write_all"] or len(all_scene_classes) == 1:
95+
classes_to_run = all_scene_classes
10596
else:
97+
name_to_class = {sc.__name__: sc for sc in all_scene_classes}
98+
classes_to_run = [name_to_class.get(name) for name in run_config.scene_names]
99+
classes_to_run = list(filter(lambda x: x, classes_to_run)) # Remove Nones
100+
note_missing_scenes(run_config.scene_names, name_to_class.keys())
101+
102+
if len(classes_to_run) == 0:
106103
classes_to_run = prompt_user_for_choice(all_scene_classes)
107104

108105
return [
@@ -111,7 +108,10 @@ def get_scenes_to_render(all_scene_classes, scene_config, run_config):
111108
]
112109

113110

114-
def get_scene_classes_from_module(module):
111+
def get_scene_classes(module: Optional[Module]):
112+
if module is None:
113+
# If no module was passed in, just play the blank scene
114+
return [BlankScene(**scene_config)]
115115
if hasattr(module, "SCENES_IN_ORDER"):
116116
return module.SCENES_IN_ORDER
117117
else:
@@ -162,24 +162,16 @@ def insert_embed_line_to_module(module: Module, line_number: int):
162162
exec(code_object, module.__dict__)
163163

164164

165-
def get_scene_module(file_name: Optional[str], embed_line: Optional[int], is_reload: bool = False) -> Module:
165+
def get_module(file_name: Optional[str], embed_line: Optional[int], is_reload: bool = False) -> Module:
166166
module = ModuleLoader.get_module(file_name, is_reload)
167167
if embed_line:
168168
insert_embed_line_to_module(module, embed_line)
169169
return module
170170

171171

172-
def main(scene_config, run_config):
173-
module = get_scene_module(
174-
run_config["file_name"],
175-
run_config["embed_line"],
176-
run_config["is_reload"]
177-
)
178-
if module is None:
179-
# If no module was passed in, just play the blank scene
180-
return [BlankScene(**scene_config)]
181-
182-
all_scene_classes = get_scene_classes_from_module(module)
172+
def main(scene_config: Dict, run_config: Dict):
173+
module = get_module(run_config.file_name, run_config.embed_line, run_config.is_reload)
174+
all_scene_classes = get_scene_classes(module)
183175
scenes = get_scenes_to_render(all_scene_classes, scene_config, run_config)
184176
if len(scenes) == 0:
185177
print("No scenes found to run")

0 commit comments

Comments
 (0)