Skip to content
Draft
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
25 changes: 11 additions & 14 deletions manim/_config/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ class MyScene(Scene): ...
}

def __init__(self) -> None:
self._d: dict[str, Any | None] = dict.fromkeys(self._OPTS)
self._d: dict[str, Any] = dict.fromkeys(self._OPTS)

# behave like a dict
def __iter__(self) -> Iterator[str]:
Expand Down Expand Up @@ -423,13 +423,13 @@ def __copy__(self) -> Self:
"""See ManimConfig.copy()."""
return copy.deepcopy(self)

def __deepcopy__(self, memo: dict[str, Any]) -> Self:
def __deepcopy__(self, memo: dict[int, Any]) -> Self:
"""See ManimConfig.copy()."""
c = type(self)()
# Deepcopying the underlying dict is enough because all properties
# either read directly from it or compute their value on the fly from
# values read directly from it.
c._d = copy.deepcopy(self._d, memo) # type: ignore[arg-type]
c._d = copy.deepcopy(self._d, memo)
return c

# helper type-checking methods
Expand Down Expand Up @@ -1142,24 +1142,22 @@ def frame_width(self, value: float) -> None:
@property
def frame_y_radius(self) -> float:
"""Half the frame height (no flag)."""
return self._d["frame_height"] / 2 # type: ignore[operator]
return self._d["frame_height"] / 2

@frame_y_radius.setter
def frame_y_radius(self, value: float) -> None:
self._d.__setitem__("frame_y_radius", value) or self._d.__setitem__( # type: ignore[func-returns-value]
"frame_height", 2 * value
)
self._d["frame_y_radius"] = value
self._d["frame_height"] = 2 * value

@property
def frame_x_radius(self) -> float:
"""Half the frame width (no flag)."""
return self._d["frame_width"] / 2 # type: ignore[operator]
return self._d["frame_width"] / 2

@frame_x_radius.setter
def frame_x_radius(self, value: float) -> None:
self._d.__setitem__("frame_x_radius", value) or self._d.__setitem__( # type: ignore[func-returns-value]
"frame_width", 2 * value
)
self._d["frame_x_radius"] = value
self._d["frame_width"] = 2 * value

@property
def top(self) -> Vector3D:
Expand Down Expand Up @@ -1290,9 +1288,8 @@ def frame_size(self) -> tuple[int, int]:

@frame_size.setter
def frame_size(self, value: tuple[int, int]) -> None:
self._d.__setitem__("pixel_width", value[0]) or self._d.__setitem__( # type: ignore[func-returns-value]
"pixel_height", value[1]
)
self._d["pixel_width"] = value[0]
self._d["pixel_height"] = value[1]

@property
def quality(self) -> str | None:
Expand Down
51 changes: 51 additions & 0 deletions manim/mobject/_fit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Fit-to-size operations for :class:`~.Mobject`.

Standalone functions that take a mobject as their first argument; the
corresponding methods on :class:`Mobject` are thin delegations to these
helpers. This keeps :class:`Mobject`'s public API unchanged while moving
the implementation out of the already-large ``mobject.py``.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

if TYPE_CHECKING:
from manim.mobject.mobject import Mobject


def rescale_to_fit(
mob: Mobject, length: float, dim: int, stretch: bool = False, **kwargs: Any
) -> Mobject:
old_length = mob.length_over_dim(dim)
if old_length == 0:
return mob
if stretch:
mob.stretch(length / old_length, dim, **kwargs)
else:
mob.scale(length / old_length, **kwargs)
return mob


def scale_to_fit_width(mob: Mobject, width: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, width, 0, stretch=False, **kwargs)


def stretch_to_fit_width(mob: Mobject, width: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, width, 0, stretch=True, **kwargs)


def scale_to_fit_height(mob: Mobject, height: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, height, 1, stretch=False, **kwargs)


def stretch_to_fit_height(mob: Mobject, height: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, height, 1, stretch=True, **kwargs)


def scale_to_fit_depth(mob: Mobject, depth: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, depth, 2, stretch=False, **kwargs)


def stretch_to_fit_depth(mob: Mobject, depth: float, **kwargs: Any) -> Mobject:
return rescale_to_fit(mob, depth, 2, stretch=True, **kwargs)
27 changes: 14 additions & 13 deletions manim/mobject/mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import numpy as np

from manim.data_structures import MethodWithArgs
from manim.mobject import _fit
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL

from .. import config, logger
Expand Down Expand Up @@ -1752,13 +1753,7 @@ def stretch_about_point(self, factor: float, dim: int, point: Point3DLike) -> Se
def rescale_to_fit(
self, length: float, dim: int, stretch: bool = False, **kwargs: Any
) -> Self:
old_length = self.length_over_dim(dim)
if old_length == 0:
return self
if stretch:
self.stretch(length / old_length, dim, **kwargs)
else:
self.scale(length / old_length, **kwargs)
_fit.rescale_to_fit(self, length, dim, stretch=stretch, **kwargs)
return self

def scale_to_fit_width(self, width: float, **kwargs: Any) -> Self:
Expand All @@ -1784,7 +1779,8 @@ def scale_to_fit_width(self, width: float, **kwargs: Any) -> Self:
>>> sq.height
np.float64(5.0)
"""
return self.rescale_to_fit(width, 0, stretch=False, **kwargs)
_fit.scale_to_fit_width(self, width, **kwargs)
return self

def stretch_to_fit_width(self, width: float, **kwargs: Any) -> Self:
"""Stretches the :class:`~.Mobject` to fit a width, not keeping height/depth proportional.
Expand All @@ -1809,7 +1805,8 @@ def stretch_to_fit_width(self, width: float, **kwargs: Any) -> Self:
>>> sq.height
np.float64(2.0)
"""
return self.rescale_to_fit(width, 0, stretch=True, **kwargs)
_fit.stretch_to_fit_width(self, width, **kwargs)
return self

def scale_to_fit_height(self, height: float, **kwargs: Any) -> Self:
"""Scales the :class:`~.Mobject` to fit a height while keeping width/depth proportional.
Expand All @@ -1834,7 +1831,8 @@ def scale_to_fit_height(self, height: float, **kwargs: Any) -> Self:
>>> sq.width
np.float64(5.0)
"""
return self.rescale_to_fit(height, 1, stretch=False, **kwargs)
_fit.scale_to_fit_height(self, height, **kwargs)
return self

def stretch_to_fit_height(self, height: float, **kwargs: Any) -> Self:
"""Stretches the :class:`~.Mobject` to fit a height, not keeping width/depth proportional.
Expand All @@ -1859,15 +1857,18 @@ def stretch_to_fit_height(self, height: float, **kwargs: Any) -> Self:
>>> sq.width
np.float64(2.0)
"""
return self.rescale_to_fit(height, 1, stretch=True, **kwargs)
_fit.stretch_to_fit_height(self, height, **kwargs)
return self

def scale_to_fit_depth(self, depth: float, **kwargs: Any) -> Self:
"""Scales the :class:`~.Mobject` to fit a depth while keeping width/height proportional."""
return self.rescale_to_fit(depth, 2, stretch=False, **kwargs)
_fit.scale_to_fit_depth(self, depth, **kwargs)
return self

def stretch_to_fit_depth(self, depth: float, **kwargs: Any) -> Self:
"""Stretches the :class:`~.Mobject` to fit a depth, not keeping width/height proportional."""
return self.rescale_to_fit(depth, 2, stretch=True, **kwargs)
_fit.stretch_to_fit_depth(self, depth, **kwargs)
return self

def set_coord(
self, value: float, dim: int, direction: Vector3DLike = ORIGIN
Expand Down
Loading