Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 23 additions & 36 deletions manim/file_writer/file_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from manim.utils.sounds import get_full_sound_file_path

if TYPE_CHECKING:
from manim.typing import PixelArray, StrOrBytesPath
from manim.typing import PixelArray, StrOrBytesPath, StrPath


def to_av_frame_rate(fps: float) -> Fraction:
Expand Down Expand Up @@ -76,8 +76,7 @@ def convert_audio(


class FileWriter(FileWriterProtocol):
"""
FileWriter is the object that actually writes the animations
"""FileWriter is the object that actually writes the animations
played, into video files, using FFMPEG.
This is mostly for Manim's internal use. You will rarely, if ever,
have to use the methods for this class, unless tinkering with the very
Expand Down Expand Up @@ -272,9 +271,11 @@ def get_resolution_directory(self) -> str:
|--Tex
|--texts
|--videos
|--<name_of_file_containing_scene>
|--<height_in_pixels_of_video>p<frame_rate>
|--<scene_name>.mp4
|--<name_of_file_containing_scene>
|--<height_in_pixels_of_video>p<frame_rate>
|--partial_movie_files
|--<scene_name>.mp4
|--<scene_name>.srt

Returns
-------
Expand All @@ -300,8 +301,7 @@ def add_audio_segment(
time: float | None = None,
gain_to_background: float | None = None,
) -> None:
"""
This method adds an audio segment from an
"""This method adds an audio segment from an
AudioSegment type object and suitable parameters.

Parameters
Expand All @@ -310,8 +310,7 @@ def add_audio_segment(
The audio segment to add

time
the timestamp at which the
sound should be added.
the timestamp at which the sound should be added.

gain_to_background
The gain of the segment from the background.
Expand Down Expand Up @@ -346,8 +345,7 @@ def add_sound(
gain: float | None = None,
**kwargs: Any,
) -> None:
"""
This method adds an audio segment from a sound file.
"""This method adds an audio segment from a sound file.

Parameters
----------
Expand Down Expand Up @@ -385,10 +383,9 @@ def add_sound(

# Writers
def begin_animation(
self, allow_write: bool = False, file_path: str | None = None
self, allow_write: bool = False, file_path: StrPath | None = None
) -> None:
"""
Used internally by manim to stream the animation to FFMPEG for
"""Used internally by manim to stream the animation to FFMPEG for
displaying or writing to a file.

Parameters
Expand All @@ -400,8 +397,7 @@ def begin_animation(
self.open_partial_movie_stream(file_path=file_path)

def end_animation(self, allow_write: bool = False) -> None:
"""
Internally used by Manim to stop streaming to
"""Internally used by Manim to stop streaming to
FFMPEG gracefully.

Parameters
Expand All @@ -423,8 +419,7 @@ def listen_and_write(self) -> None:
self.encode_and_write_frame(frame_data, num_frames)

def encode_and_write_frame(self, frame: PixelArray, num_frames: int) -> None:
"""
For internal use only: takes a given frame in ``np.ndarray`` format and
"""For internal use only: takes a given frame in ``np.ndarray`` format and
write it to the stream
"""
for _ in range(num_frames):
Expand All @@ -439,9 +434,7 @@ def encode_and_write_frame(self, frame: PixelArray, num_frames: int) -> None:
self.video_container.mux(packet)

def write_frame(self, frame: PixelArray, num_frames: int = 1) -> None:
"""
Used internally by Manim to write a frame to
the FFMPEG input buffer.
"""Used internally by Manim to write a frame to the FFMPEG input buffer.

Parameters
----------
Expand All @@ -466,7 +459,7 @@ def write_frame(self, frame: PixelArray, num_frames: int = 1) -> None:
)

def output_image(
self, image: Image.Image, target_dir: str | Path, ext: str, zero_pad: int
self, image: Image.Image, target_dir: StrPath, ext: str, zero_pad: int
) -> None:
if zero_pad:
image.save(f"{target_dir}{str(self.frame_count).zfill(zero_pad)}{ext}")
Expand All @@ -475,8 +468,7 @@ def output_image(
self.frame_count += 1

def save_image(self, image: PixelArray) -> None:
"""
Saves an image in the default image directory.
"""Saves an image in the default image directory.

Parameters
----------
Expand All @@ -491,13 +483,9 @@ def save_image(self, image: PixelArray) -> None:
self.print_file_ready_message(self.image_file_path)

def finish(self) -> None:
"""
Finishes writing to the FFMPEG buffer or writing images
to output directory.
Combines the partial movie files into the
whole scene.
If save_last_frame is True, saves the last
frame in the default image directory.
"""Finishes writing to the FFMPEG buffer or writing images to output directory.
Combines the partial movie files into the whole scene.
If save_last_frame is True, saves the last frame in the default image directory.
"""
if write_to_movie():
self.combine_to_movie()
Expand All @@ -513,7 +501,7 @@ def finish(self) -> None:
if self.subcaptions:
self.write_subcaption_file()

def open_partial_movie_stream(self, file_path: str | None = None) -> None:
def open_partial_movie_stream(self, file_path: StrPath | None = None) -> None:
"""Open a container holding a video stream.

This is used internally by Manim initialize the container holding
Expand Down Expand Up @@ -640,8 +628,7 @@ def combine_files(
if config.transparent and config.movie_file_extension == ".webm":
output_stream.pix_fmt = "yuva420p"
if create_gif:
"""
The following solution was largely inspired from this comment
"""The following solution was largely inspired from this comment
https://github.com/imageio/imageio/issues/995#issuecomment-1580533018,
and the following code
https://github.com/imageio/imageio/blob/65d79140018bb7c64c0692ea72cb4093e8d632a0/imageio/plugins/pyav.py#L927-L996.
Expand Down Expand Up @@ -869,7 +856,7 @@ def write_subcaption_file(self) -> None:
subcaption_file.write_text(srt.compose(self.subcaptions), encoding="utf-8")
logger.info(f"Subcaption file has been written as {subcaption_file}")

def print_file_ready_message(self, file_path: str | Path) -> None:
def print_file_ready_message(self, file_path: StrPath) -> None:
"""Prints the "File Ready" message to STDOUT."""
config.output_file = str(file_path)
logger.info(f"\nFile ready at {str(file_path)!r}\n")
8 changes: 4 additions & 4 deletions manim/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import platform
import time
import warnings
from collections.abc import Iterable, Iterator, Sequence
from typing import TYPE_CHECKING, Callable, Generic, TypeVar
from collections.abc import Callable, Iterable, Iterator, Sequence
from typing import TYPE_CHECKING, Generic, TypeVar

import numpy as np

Expand Down Expand Up @@ -108,10 +108,10 @@ def create_window(self) -> WindowProtocol | None:
-------
A window if previewing, else None
"""
return Window() if config.preview else None # type: ignore[abstract]
return Window() if config.preview else None

def create_file_writer(self) -> FileWriterProtocol:
"""Create and returna file writer instance.
"""Create and return a file writer instance.

This can be overridden in subclasses (plugins), if more
processing is needed.
Expand Down
9 changes: 5 additions & 4 deletions manim/mobject/geometry/shape_matchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from manim.mobject.geometry.line import Line
from manim.mobject.geometry.polygram import RoundedRectangle
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_mobject import OpenGLMobject
from manim.mobject.types.vectorized_mobject import VGroup
from manim.utils.color import BLACK, RED, YELLOW, ManimColor, ParsableManimColor

Expand Down Expand Up @@ -50,17 +51,17 @@ def construct(self):

def __init__(
self,
*mobjects: Mobject,
*mobjects: Mobject | OpenGLMobject,
color: ParsableManimColor = YELLOW,
buff: float = SMALL_BUFF,
corner_radius: float = 0.0,
**kwargs: Any,
) -> None:
from manim.mobject.mobject import Group

if not all(isinstance(mob, Mobject) for mob in mobjects):
if not all(isinstance(mob, (Mobject, OpenGLMobject)) for mob in mobjects):
raise TypeError(
"Expected all inputs for parameter mobjects to be a Mobjects"
"Expected all inputs for parameter mobjects to be of type Mobject or OpenGLMobject"
)

group = Group(*mobjects)
Expand Down Expand Up @@ -122,7 +123,7 @@ def __init__(
buff=buff,
**kwargs,
)
self.original_fill_opacity: float = self.fill_opacity
self.original_fill_opacity: float = self.get_fill_opacity()

def pointwise_become_partial(self, mobject: Mobject, a: Any, b: float) -> Self:
self.set_fill(opacity=b * self.original_fill_opacity)
Expand Down
4 changes: 2 additions & 2 deletions manim/mobject/opengl/shader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
import moderngl
import numpy as np

from .. import config
from ..utils import opengl
from ... import config
from ...utils import opengl

SHADER_FOLDER = Path(__file__).parent / "shaders"
shader_program_cache: dict = {}
Expand Down
2 changes: 1 addition & 1 deletion manim/utils/ipython_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from manim import config, logger, tempconfig
from manim.__main__ import main
from manim.manager import Manager
from manim.renderer.shader import shader_program_cache
from manim.mobject.opengl.shader import shader_program_cache

__all__ = ["ManimMagic"]

Expand Down
6 changes: 2 additions & 4 deletions manim/utils/space_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,8 +598,7 @@ def find_intersection(
v1s: Sequence[np.ndarray] | Point3D_Array,
threshold: float = 1e-5,
) -> Sequence[np.ndarray]:
"""
Return the intersection of a line passing through p0 in direction v0
"""Return the intersection of a line passing through p0 in direction v0
with one passing through p1 in direction v1 (or array of intersections
from arrays of such points/directions).
For 3d values, it returns the point on the ray p0 + v0 * t closest to the
Expand Down Expand Up @@ -660,8 +659,7 @@ def shoelace(x_y: np.ndarray) -> float:


def shoelace_direction(x_y: np.ndarray) -> str:
"""
Uses the area determined by the shoelace method to determine whether
"""Uses the area determined by the shoelace method to determine whether
the input set of points is directed clockwise or counterclockwise.

Returns
Expand Down
4 changes: 2 additions & 2 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ ignore_errors = True
ignore_errors = True

[mypy-manim.cli.*]
ignore_errors = False
ignore_errors = True

[mypy-manim.cli.cfg.*]
ignore_errors = False
Expand All @@ -71,7 +71,7 @@ ignore_errors = True
ignore_errors = True

[mypy-manim.mobject.geometry.*]
ignore_errors = False
ignore_errors = True

[mypy-manim.renderer.*]
ignore_errors = True
Expand Down
2 changes: 1 addition & 1 deletion tests/test_scene_rendering/test_file_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest

from manim import DR, Circle, Create, Manager, Scene, Star
from manim.scene.scene_file_writer import to_av_frame_rate
from manim.file_writer.file_writer import to_av_frame_rate
from manim.utils.commands import capture, get_video_metadata


Expand Down