github ManimCommunity/manim v0.22.0
Manim v0.22.0

4 hours ago

See our rendered changelog.

Highlights ๐ŸŒŸ

Unified output selection and configurable video encoding

Choosing what Manim produces has been reworked around a single format
setting. --format (or format in the [CLI] section of a config file)
selects exactly one primary artifact: auto (the default; MP4, or MOV for
transparent output), mp4, mov, webm, gif, png (last frame only),
png-sequence (every frame), or none (evaluate the scene without writing
media). It replaces the overlapping --write_to_movie, -g/--save_pngs,
-i/--save_as_gif, and --force_window options. Viewing the result is now
separate from choosing what to write: -p/--preview opens the finished file
with either renderer, while -l/--live-preview shows a live window (OpenGL).
As a consequence, the OpenGL renderer now writes files by default, just like
Cairo. The --custom_folders preset has been removed in favor of setting
each output location separately. (#4966, #4969)

Video encoding is now configurable, too: the new [video_encoder] config
section and the --video-codec, --pixel-format, and repeatable
--encoder-option KEY=VALUE options control how partial movie files are
encoded, for example
manim render --video-codec libx264 --encoder-option crf=18 --encoder-option preset=slow scene.py.
The new manim cache clear command replaces the --flush_cache flag. (#4976)

Scenes rendered with default settings need no changes, but scripts and config
files relying on the removed options have to be updated. See the migration
notes in the Breaking Changes section below.

Comprehensive documentation for the updated configuration system
can be found in the
configuration guide and the
output settings tutorial.

A Manager for coordinating the render flow

This release takes a first step towards a full overhaul of Manim's
render lifecycle by introducing a Manager object that coordinates
the processing of a Scene. Regular scenes (i.e., those without
custom cameras, renderers, or file writers) do
not need any changes.

Until now, the responsibilities of the render lifecycle were spread
across Scene, the renderer, the camera, and the file writer.
As the name suggests, the Manager is now the central manager of this process.
This release lays the foundation for the new architecture. The render
loop has not been fully restructured yet.
Upcoming releases will build on this to move rendering execution
and file writing into the Manager.
This will allow us to remove the remaining cross-dependencies between
scenes, renderers, and file writing.

The rendering deep dive has been updated to
describe the new flow. (#4916)

Improved rendering of Text etc. with small font size

This release also features a new version of our plain text rendering
engine (Pango) wrapper ManimPango that fixes a long-standing issue with the kerning
(spacing between letters) for text with very small font sizes. Now
mobjects like Text("hello world, this is a test", font_size=1) look crisp
and are typeset as intended. (#5041)

What's Changed

Breaking Changes ๐Ÿšจ

  • Introduce a Manager to coordinate the render flow by @behackl in #4916

    Scenes, including overrides of setup, construct, and tear_down, need no
    changes. Scene.render, Scene.play, Scene.next_section,
    Scene.add_subcaption, and Scene.add_sound now delegate to the scene's
    Manager (scene.manager), which owns the render lifecycle formerly
    implemented in Scene.render. Code that reimplemented or patched these
    internals should call the public Scene methods instead of driving the
    renderer or file writer directly. The "Rendered โ€ฆ Played N animations" log
    record now originates from the manager module instead of scene.

  • Rework output configuration options and how they are consumed by @behackl in #4966

    Output is now selected with the single format option; the old switches have
    been removed:

    • --write_to_movie / write_to_movie = True: use the default
      format = auto or an explicit mp4, mov, webm, or gif;
      write_to_movie = False becomes format = none.
    • -g / --save_pngs: --format png-sequence (frames are written to
      media/images/<module>/<Scene>/).
    • -i / --save_as_gif: --format gif.
    • save_last_frame in config files: format = png. The -s flag and
      config.save_last_frame still work.
    • --force_window: -l / --live-preview; add a concrete format to also
      record the live render.
    • -f: --show_in_file_browser. config.movie_file_extension: config.format.

    Behavior changes: --format png saves only the last frame; an explicit video
    format fails for scenes without play or wait calls (auto saves a PNG
    instead); with OpenGL, -p opens the finished file instead of a live window
    and files are written by default; transparent output requires auto, mov,
    or webm. Output settings are captured when a Scene is constructed, so
    create scenes inside the intended tempconfig block. Rendering no longer
    overwrites config.output_file; read
    scene.renderer.file_writer.final_file_path instead. The format helpers
    is_mp4_format, is_gif_format, is_png_format, is_webm_format,
    is_mov_format, and write_to_movie were removed from
    manim.utils.file_ops; inspect scene.session_spec or
    scene.manager.output_spec instead. For custom renderers and file writers,
    see #4976.

  • Refactor media output path resolution and remove the --custom_folders CLI flag and config option by @behackl in #4969

    --custom_folders, config.custom_folders, and the [custom_folders] config
    section have been removed; set the regular directory options under [CLI]
    instead. The former preset corresponds to:

    [CLI]
    media_dir = videos
    video_dir = {media_dir}
    sections_dir = {media_dir}
    images_dir = {media_dir}
    text_dir = {media_dir}/temp_files
    tex_dir = {media_dir}/temp_files
    log_dir = {media_dir}/temp_files
    partial_movie_dir = {media_dir}/partial_movie_files/{scene_name}

    -o / --output_file is now rejected when rendering more than one scene
    (with -a or several scene names); drop it or render scenes separately. The
    resolved format determines the file suffix, so -o movie.mov --format mp4
    produces movie.mov.mp4. Section videos now include the section name (e.g.
    MyScene_0000_create-square.mp4); read filenames from the video field of
    the section JSON instead of constructing them.

  • Move video segment encoding to a dedicated module and detach SceneFileWriter from renderer state by @behackl in #4976

    Replace --flush_cache and flush_cache = True with
    manim cache clear scene.py SceneName. The [ffmpeg] config section is now
    [media] (loglevel stays the same), and config.ffmpeg_loglevel is now
    config.media_loglevel. Cache keys now include the encoder settings, so
    existing cached segments are re-rendered once. SceneFileWriter.clean_cache()
    and flush_cache_directory() were replaced by prune_segment_cache() and
    clear_segment_cache() in manim.utils.caching.

    Custom renderers and file writers: init_scene now has the signature
    init_scene(scene, session_spec, file_writer_settings), and the writer is
    constructed from these settings alone (it no longer receives the renderer or
    reads the global config). Renderers must declare capabilities. Frames are
    passed as owned uint8 RGBA arrays of shape (height, width, 4) with
    top-left origin: write_frame(pixels, repeat=n) and save_image(pixels);
    begin_animation() and open_partial_movie_stream() additionally require
    animation_index=. get_hash_from_play_call() requires the backend,
    encoder_fingerprint, and renderer_state keyword arguments, and
    get_full_sound_file_path() requires assets_dir.

Enhancements ๐Ÿš€

  • Improve the error message of throw_error_if_no_points and raise ValueError instead of Exception by @smmariquit in #4927
  • Add a key parameter to list_update and list_difference_update to support unhashable elements by @nikolajmunk in #4975

Bug Fixes ๐Ÿ›

  • Fix OpenGL-specific code paths that were skipped because renderer comparisons against the string "opengl" never matched by @pjfo in #4929
  • Fix mobject duplication during animations by treating ancestors of moving mobjects as moving by @nikolajmunk in #4936
  • Allow Graph and DiGraph to be initialized without vertices by @christopher-hampson in #4946
  • Fix singular/plural form of "submobject(s)" in VGroup.__str__ by @nikolajmunk in #4950
  • Fix right-bounds check in OpenGLMobject.is_off_screen by @GniLudio in #4953
  • Fix OpenGLMobject.stretch_to_fit_depth stretching along the wrong dimension by @GniLudio in #4955
  • Fix OpenGLMobject.get_boundary_point by @GniLudio in #4956
  • Restore renderer class bases when updating the config from another ManimConfig by @emerardd in #4958
  • Skip hidden files and tolerate vanishing files when cleaning the partial movie file cache by @changsheng0804-blip in #4954
  • Fix LaggedStartMap unpacking submobjects when using the default arg_creator by @SAY-5 in #4965
  • Replace deprecated np.sum calls on generators, fixing a TypeError with the OpenGL renderer by @christopher-hampson in #4962
  • Preserve the layout of MathTypst capture groups by @behackl in #4960
  • Fix normalize raising an error instead of returning the fallback vector for zero-norm vectors by @nikolajmunk in #5001
  • Fix is_closed misclassifying closed curves in negative coordinate space by @Osamaali313 in #5010
  • Fix angle_axis_from_quaternion returning the inverse rotation for angles greater than ฯ€ by @dylanpulver in #4980
  • Fix LineJointType.AUTO and CapStyleType.AUTO inheriting the previous mobject's style in the Cairo renderer by @RizgarOzan in #5016
  • Fix BarChart ignoring include_numbers in y_axis_config by @RizgarOzan in #5015
  • Fix graph label placement and coordinate conversion on logarithmic axes by @GreedyC in #5039
  • Fix SVG shapes with stroke="none" getting a white outline by @RizgarOzan in #5036
  • Fix color argument of Text and MarkupText not being applied to the mobject itself by @webzuweb in #5017
  • Fix the redirections parameter of the deprecated_params decorator by @GniLudio in #5028
  • Fix rendering WebM videos with sound when PyAV has no libvorbis encoder by @Darkslayer3324j in #5025
  • Fix ThreeDScene.set_camera_orientation with the OpenGL renderer by @Smail in #5040
  • Fix Mobject.insert and OpenGLMobject.replace_submobject permitting duplicate submobjects by @nikolajmunk in #4957

Performance โšก

  • Speed up list_update and list_difference_update by using a set for membership checks by @nikolajmunk in #4939
  • Speed up redundancy removal in get_family and fix families containing loops by @GniLudio in #4947
  • Improve speed and robustness of Mobject and OpenGLMobject add/remove operations by @nikolajmunk and @GniLudio in #4957
  • Bound the encoder frame queue when rendering without parallel encoding to limit memory usage by @pizzaburgare in #5049

Documentation ๐Ÿ“š

Testing ๐Ÿงช

Type Hints ๐Ÿ“

  • Use ParamSpec in deprecation decorators to preserve signatures of decorated callables by @GniLudio in #4988
  • Add type hints to vector_field.py by @henrikmidtiby in #5030

Dependencies ๐Ÿ“ฆ

Other Changes

New Contributors

Full Changelog: Compare view

Don't miss a new manim release

NewReleases is sending notifications on new releases.