mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Merge branch 'main' into guide
This commit is contained in:
commit
978b3e71b4
23 changed files with 859 additions and 950 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -34,7 +34,7 @@ jobs:
|
|||
poetry config virtualenvs.prefer-active-python true
|
||||
|
||||
- name: Setup Python ${{ matrix.python }}
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
cache: "poetry"
|
||||
|
|
@ -143,7 +143,7 @@ jobs:
|
|||
$tinyTexPackages = $(python -c "import json;print(' '.join(json.load(open('.github/manimdependency.json'))['windows']['tinytex']))") -Split ' '
|
||||
$OriPath = $env:PATH
|
||||
echo "Install Tinytex"
|
||||
Invoke-WebRequest "https://github.com/yihui/tinytex-releases/releases/download/daily/TinyTeX-1.zip" -O "$($env:TMP)\TinyTex.zip"
|
||||
Invoke-WebRequest "https://github.com/yihui/tinytex-releases/releases/download/daily/TinyTeX-1.zip" -OutFile "$($env:TMP)\TinyTex.zip"
|
||||
Expand-Archive -LiteralPath "$($env:TMP)\TinyTex.zip" -DestinationPath "$($PWD)\ManimCache\LatexWindows"
|
||||
$env:Path = "$($PWD)\ManimCache\LatexWindows\TinyTeX\bin\windows;$($env:PATH)"
|
||||
tlmgr update --self
|
||||
|
|
|
|||
4
.github/workflows/python-publish.yml
vendored
4
.github/workflows/python-publish.yml
vendored
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
poetry build
|
||||
|
||||
- name: Store artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: dist/*.tar.gz
|
||||
name: manim.tar.gz
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python 3.11
|
||||
uses: actions/setup-python@v4
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ jobs:
|
|||
tar -czvf ../html-docs.tar.gz *
|
||||
|
||||
- name: Store artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/docs/build/html-docs.tar.gz
|
||||
name: html-docs.tar.gz
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ Typing guidelines
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.typing import Vector3
|
||||
# type stuff with Vector3
|
||||
from manim.typing import Vector3D
|
||||
# type stuff with Vector3D
|
||||
|
||||
Missing Sections for typehints are:
|
||||
-----------------------------------
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ Basic Concepts
|
|||
self.add(image, image.background_rectangle)
|
||||
|
||||
.. manim:: BooleanOperations
|
||||
:ref_classes: Union Intersection Exclusion
|
||||
:ref_classes: Union Intersection Exclusion Difference
|
||||
|
||||
class BooleanOperations(Scene):
|
||||
def construct(self):
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ modify to your liking. First, run
|
|||
|
||||
.. code-block:: sh
|
||||
|
||||
docker run -it --name my-manim-container -v "/full/path/to/your/directory:/manim" manimcommunity/manim /bin/bash
|
||||
docker run -it --name my-manim-container -v "/full/path/to/your/directory:/manim" manimcommunity/manim bash
|
||||
|
||||
|
||||
to obtain an interactive shell inside your container allowing you
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ then execute it.
|
|||
texlive-latex-recommended texlive-science \
|
||||
tipa libpango1.0-dev
|
||||
!pip install manim
|
||||
!pip install IPython --upgrade
|
||||
!pip install IPython==8.21.0
|
||||
|
||||
You should start to see Colab installing all the dependencies specified
|
||||
in these commands. After the execution has completed, you will be prompted
|
||||
|
|
|
|||
|
|
@ -99,40 +99,18 @@ directory structure, build system, and naming are completely up to your
|
|||
discretion as an author. The aforementioned template plugin is only a model
|
||||
using Poetry since this is the build system Manim uses. The plugin's `entry
|
||||
point <https://packaging.python.org/specifications/entry-points/>`_ can be
|
||||
specified in poetry as:
|
||||
specified in Poetry as:
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
"name" = "object_reference"
|
||||
|
||||
Here ``name`` is the name of the module of the plugin.
|
||||
.. versionremoved:: 0.19.0
|
||||
|
||||
Here ``object_reference`` can point to either a function in a module or a module
|
||||
itself. For example,
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
"manim_plugintemplate" = "manim_plugintemplate"
|
||||
|
||||
Here a module is used as ``object_reference``, and when this plugin is enabled,
|
||||
Manim will look for ``__all__`` keyword defined in ``manim_plugintemplate`` and
|
||||
everything as a global variable one by one.
|
||||
|
||||
If ``object_reference`` is a function, Manim calls the function and expects the
|
||||
function to return a list of modules or functions that need to be defined globally.
|
||||
|
||||
For example,
|
||||
|
||||
.. code-block:: toml
|
||||
|
||||
[tool.poetry.plugins."manim.plugins"]
|
||||
"manim_plugintemplate" = "manim_awesomeplugin.imports:setup_things"
|
||||
|
||||
Here, Manim will call the function ``setup_things`` defined in
|
||||
``manim_awesomeplugin.imports`` and calls that. It returns a list of function or
|
||||
modules which will be imported globally.
|
||||
Plugins should be imported explicitly to be usable in user code. The plugin
|
||||
system will probably be refactored in the future to provide a more structured
|
||||
interface.
|
||||
|
||||
A note on Renderer Compatibility
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class Indicate(Transform):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
mobject: Mobject,
|
||||
scale_factor: float = 1.2,
|
||||
color: str = YELLOW,
|
||||
rate_func: Callable[[float, Optional[float]], np.ndarray] = there_and_back,
|
||||
|
|
@ -158,7 +158,7 @@ class Indicate(Transform):
|
|||
self.scale_factor = scale_factor
|
||||
super().__init__(mobject, rate_func=rate_func, **kwargs)
|
||||
|
||||
def create_target(self) -> "Mobject":
|
||||
def create_target(self) -> Mobject:
|
||||
target = self.mobject.copy()
|
||||
target.scale(self.scale_factor)
|
||||
target.set_color(self.color)
|
||||
|
|
@ -348,7 +348,7 @@ class ShowPassingFlashWithThinningStrokeWidth(AnimationGroup):
|
|||
message="Use Create then FadeOut to achieve this effect.",
|
||||
)
|
||||
class ShowCreationThenFadeOut(Succession):
|
||||
def __init__(self, mobject: "Mobject", remover: bool = True, **kwargs) -> None:
|
||||
def __init__(self, mobject: Mobject, remover: bool = True, **kwargs) -> None:
|
||||
super().__init__(Create(mobject), FadeOut(mobject), remover=remover, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -397,7 +397,7 @@ class ApplyWave(Homotopy):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
mobject: Mobject,
|
||||
direction: np.ndarray = UP,
|
||||
amplitude: float = 0.2,
|
||||
wave_func: Callable[[float], float] = smooth,
|
||||
|
|
@ -516,7 +516,7 @@ class Wiggle(Animation):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject: "Mobject",
|
||||
mobject: Mobject,
|
||||
scale_value: float = 1.1,
|
||||
rotation_angle: float = 0.01 * TAU,
|
||||
n_wiggles: int = 6,
|
||||
|
|
@ -544,8 +544,8 @@ class Wiggle(Animation):
|
|||
|
||||
def interpolate_submobject(
|
||||
self,
|
||||
submobject: "Mobject",
|
||||
starting_submobject: "Mobject",
|
||||
submobject: Mobject,
|
||||
starting_submobject: Mobject,
|
||||
alpha: float,
|
||||
) -> None:
|
||||
submobject.points[:, :] = starting_submobject.points
|
||||
|
|
|
|||
|
|
@ -59,7 +59,13 @@ render_options = option_group(
|
|||
type=Choice(["png", "gif", "mp4", "webm", "mov"], case_sensitive=False),
|
||||
default=None,
|
||||
),
|
||||
option("-s", "--save_last_frame", is_flag=True, default=None),
|
||||
option(
|
||||
"-s",
|
||||
"--save_last_frame",
|
||||
default=None,
|
||||
is_flag=True,
|
||||
help="Render and save only the last frame of a scene as a PNG image.",
|
||||
),
|
||||
option(
|
||||
"-q",
|
||||
"--quality",
|
||||
|
|
@ -123,13 +129,6 @@ render_options = option_group(
|
|||
is_flag=True,
|
||||
help="Save section videos in addition to movie file.",
|
||||
),
|
||||
option(
|
||||
"-s",
|
||||
"--save_last_frame",
|
||||
default=None,
|
||||
is_flag=True,
|
||||
help="Save last frame as png (Deprecated).",
|
||||
),
|
||||
option(
|
||||
"-t",
|
||||
"--transparent",
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ class Arc(TipableVMobject):
|
|||
# For a1 and a2 to lie at the same point arc radius
|
||||
# must be zero. Thus arc_center will also lie at
|
||||
# that point.
|
||||
return a1
|
||||
return np.copy(a1)
|
||||
# Tangent vectors
|
||||
t1 = h1 - a1
|
||||
t2 = h2 - a2
|
||||
|
|
|
|||
|
|
@ -444,8 +444,21 @@ class Text(SVGMobject):
|
|||
**kwargs,
|
||||
) -> None:
|
||||
self.line_spacing = line_spacing
|
||||
if font and warn_missing_font and font not in Text.font_list():
|
||||
logger.warning(f"Font {font} not in {Text.font_list()}.")
|
||||
if font and warn_missing_font:
|
||||
fonts_list = Text.font_list()
|
||||
# handle special case of sans/sans-serif
|
||||
if font.lower() == "sans-serif":
|
||||
font = "sans"
|
||||
if font not in fonts_list:
|
||||
# check if the capitalized version is in the supported fonts
|
||||
if font.capitalize() in fonts_list:
|
||||
font = font.capitalize()
|
||||
elif font.lower() in fonts_list:
|
||||
font = font.lower()
|
||||
elif font.title() in fonts_list:
|
||||
font = font.title()
|
||||
else:
|
||||
logger.warning(f"Font {font} not in {fonts_list}.")
|
||||
self.font = font
|
||||
self._font_size = float(font_size)
|
||||
# needs to be a float or else size is inflated when font_size = 24
|
||||
|
|
@ -1169,8 +1182,21 @@ class MarkupText(SVGMobject):
|
|||
) -> None:
|
||||
self.text = text
|
||||
self.line_spacing = line_spacing
|
||||
if font and warn_missing_font and font not in Text.font_list():
|
||||
logger.warning(f"Font {font} not in {Text.font_list()}.")
|
||||
if font and warn_missing_font:
|
||||
fonts_list = Text.font_list()
|
||||
# handle special case of sans/sans-serif
|
||||
if font.lower() == "sans-serif":
|
||||
font = "sans"
|
||||
if font not in fonts_list:
|
||||
# check if the capitalized version is in the supported fonts
|
||||
if font.capitalize() in fonts_list:
|
||||
font = font.capitalize()
|
||||
elif font.lower() in fonts_list:
|
||||
font = font.lower()
|
||||
elif font.title() in fonts_list:
|
||||
font = font.title()
|
||||
else:
|
||||
logger.warning(f"Font {font} not in {fonts_list}.")
|
||||
self.font = font
|
||||
self._font_size = float(font_size)
|
||||
self.slant = slant
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
|
|||
self.wait(1)
|
||||
tracker -= 4
|
||||
self.wait(0.5)
|
||||
self.play(tracker.animate.set_value(5)),
|
||||
self.play(tracker.animate.set_value(5))
|
||||
self.wait(0.5)
|
||||
self.play(tracker.animate.set_value(3))
|
||||
self.play(tracker.animate.increment_value(-2))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .import_plugins import *
|
||||
from manim import config, logger
|
||||
|
||||
from .plugins_flags import get_plugins, list_plugins
|
||||
|
||||
__all__ = [
|
||||
"get_plugins",
|
||||
"list_plugins",
|
||||
]
|
||||
|
||||
requested_plugins: set[str] = set(config["plugins"])
|
||||
missing_plugins = requested_plugins - set(get_plugins().keys())
|
||||
|
||||
|
||||
if missing_plugins:
|
||||
logger.warning("Missing Plugins: %s", missing_plugins)
|
||||
|
|
|
|||
|
|
@ -1,43 +0,0 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import types
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from .. import config, logger
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
plugins_requested: list[str] = config["plugins"]
|
||||
if "" in plugins_requested:
|
||||
plugins_requested.remove("")
|
||||
for plugin in pkg_resources.iter_entry_points("manim.plugins"):
|
||||
if plugin.name not in plugins_requested:
|
||||
continue
|
||||
loaded_plugin = plugin.load()
|
||||
if isinstance(loaded_plugin, types.ModuleType):
|
||||
# it is a module so it can't be called
|
||||
# see if __all__ is defined
|
||||
# if it is defined use that to load all the modules necessary
|
||||
# essentially this would be similar to `from plugin import *``
|
||||
# if not just import the module with the plugin name
|
||||
if hasattr(loaded_plugin, "__all__"):
|
||||
for thing in loaded_plugin.__all__: # type: ignore
|
||||
exec(f"{thing}=loaded_plugin.{thing}")
|
||||
__all__.append(thing)
|
||||
else:
|
||||
exec(f"{plugin.name}=loaded_plugin")
|
||||
__all__.append(plugin.name)
|
||||
elif isinstance(loaded_plugin, types.FunctionType):
|
||||
# call the function first
|
||||
# it will return a list of modules to add globally
|
||||
# finally add it
|
||||
lists = loaded_plugin()
|
||||
for lst in lists:
|
||||
exec(f"{lst.__name__}=lst")
|
||||
__all__.append(lst.__name__)
|
||||
plugins_requested.remove(plugin.name)
|
||||
|
||||
if plugins_requested != []:
|
||||
logger.warning("Missing Plugins: %s", plugins_requested)
|
||||
|
|
@ -2,22 +2,28 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import pkg_resources
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
from importlib_metadata import entry_points
|
||||
else:
|
||||
from importlib.metadata import entry_points
|
||||
|
||||
from manim import console
|
||||
|
||||
__all__ = ["list_plugins"]
|
||||
|
||||
|
||||
def get_plugins():
|
||||
plugins = {
|
||||
def get_plugins() -> dict[str, Any]:
|
||||
plugins: dict[str, Any] = {
|
||||
entry_point.name: entry_point.load()
|
||||
for entry_point in pkg_resources.iter_entry_points("manim.plugins")
|
||||
for entry_point in entry_points(group="manim.plugins")
|
||||
}
|
||||
return plugins
|
||||
|
||||
|
||||
def list_plugins():
|
||||
def list_plugins() -> None:
|
||||
console.print("[green bold]Plugins:[/green bold]", justify="left")
|
||||
|
||||
plugins = get_plugins()
|
||||
|
|
|
|||
|
|
@ -725,6 +725,8 @@ class SceneFileWriter:
|
|||
|
||||
def write_subcaption_file(self):
|
||||
"""Writes the subcaption file."""
|
||||
if config.output_file is None:
|
||||
return
|
||||
subcaption_file = Path(config.output_file).with_suffix(".srt")
|
||||
subcaption_file.write_text(srt.compose(self.subcaptions), encoding="utf-8")
|
||||
logger.info(f"Subcaption file has been written as {subcaption_file}")
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from os import PathLike
|
||||
from typing import Callable, Literal, Union
|
||||
from typing import Callable, Union
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
|
|
@ -257,9 +257,9 @@ parameter can handle being passed a `Point3D` instead.
|
|||
"""
|
||||
|
||||
InternalPoint2D_Array: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (N, 3)``
|
||||
"""``shape: (N, 2)``
|
||||
|
||||
An array of `Point2D` objects: ``[[float, float], ...]``.
|
||||
An array of `InternalPoint2D` objects: ``[[float, float], ...]``.
|
||||
|
||||
.. note::
|
||||
This type alias is mostly made available for internal use, and
|
||||
|
|
@ -319,7 +319,7 @@ further type information.
|
|||
Vector types
|
||||
"""
|
||||
|
||||
Vector2D: TypeAlias = Point2D
|
||||
Vector2D: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (2,)``
|
||||
|
||||
A 2-dimensional vector: ``[float, float]``.
|
||||
|
|
@ -332,7 +332,7 @@ parameter can handle being passed a `Vector3D` instead.
|
|||
VMobjects!
|
||||
"""
|
||||
|
||||
Vector2D_Array: TypeAlias = Point2D_Array
|
||||
Vector2D_Array: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (M, 2)``
|
||||
|
||||
An array of `Vector2D` objects: ``[[float, float], ...]``.
|
||||
|
|
@ -341,7 +341,7 @@ Normally, a function or method which expects a `Vector2D_Array` as a
|
|||
parameter can handle being passed a `Vector3D_Array` instead.
|
||||
"""
|
||||
|
||||
Vector3D: TypeAlias = Point3D
|
||||
Vector3D: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (3,)``
|
||||
|
||||
A 3-dimensional vector: ``[float, float, float]``.
|
||||
|
|
@ -351,13 +351,13 @@ A 3-dimensional vector: ``[float, float, float]``.
|
|||
VMobjects!
|
||||
"""
|
||||
|
||||
Vector3D_Array: TypeAlias = Point3D_Array
|
||||
Vector3D_Array: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (M, 3)``
|
||||
|
||||
An array of `Vector3D` objects: ``[[float, float, float], ...]``.
|
||||
"""
|
||||
|
||||
VectorND: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, ...]]
|
||||
VectorND: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape (N,)``
|
||||
|
||||
An :math:`N`-dimensional vector: ``[float, ...]``.
|
||||
|
|
@ -368,19 +368,19 @@ An :math:`N`-dimensional vector: ``[float, ...]``.
|
|||
collisions.
|
||||
"""
|
||||
|
||||
VectorND_Array: TypeAlias = Union[npt.NDArray[PointDType], tuple[VectorND, ...]]
|
||||
VectorND_Array: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape (M, N)``
|
||||
|
||||
An array of `VectorND` objects: ``[[float, ...], ...]``.
|
||||
"""
|
||||
|
||||
RowVector: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float, ...]]]
|
||||
RowVector: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (1, N)``
|
||||
|
||||
A row vector: ``[[float, ...]]``.
|
||||
"""
|
||||
|
||||
ColVector: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float], ...]]
|
||||
ColVector: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (N, 1)``
|
||||
|
||||
A column vector: ``[[float], [float], ...]``.
|
||||
|
|
@ -392,13 +392,13 @@ A column vector: ``[[float], [float], ...]``.
|
|||
Matrix types
|
||||
"""
|
||||
|
||||
MatrixMN: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float, ...], ...]]
|
||||
MatrixMN: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (M, N)``
|
||||
|
||||
A matrix: ``[[float, ...], [float, ...], ...]``.
|
||||
"""
|
||||
|
||||
Zeros: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[Literal[0], ...], ...]]
|
||||
Zeros: TypeAlias = MatrixMN
|
||||
"""``shape: (M, N)``
|
||||
|
||||
A `MatrixMN` filled with zeros, typically created with
|
||||
|
|
|
|||
1436
poetry.lock
generated
1436
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -32,6 +32,7 @@ click = ">=8.0"
|
|||
cloup = ">=2.0.0"
|
||||
dearpygui = { version = ">=1.0.0", optional = true }
|
||||
decorator = ">=4.3.2"
|
||||
importlib-metadata = {version = ">=3.6", python = "<=3.9"} # Required to discover plugins
|
||||
isosurfaces = ">=0.1.0"
|
||||
jupyterlab = { version = ">=3.0.0", optional = true }
|
||||
manimpango = ">=0.5.0,<1.0.0" # Complete API change in 1.0.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from contextlib import redirect_stdout
|
||||
from io import StringIO
|
||||
|
||||
from manim.mobject.text.text_mobject import MarkupText, Text
|
||||
|
||||
|
||||
|
|
@ -11,3 +14,20 @@ def test_font_size():
|
|||
|
||||
assert round(text_string.font_size, 5) == 14.4
|
||||
assert round(markuptext_string.font_size, 5) == 14.4
|
||||
|
||||
|
||||
def test_font_warnings():
|
||||
def warning_printed(font: str, **kwargs) -> bool:
|
||||
io = StringIO()
|
||||
with redirect_stdout(io):
|
||||
Text("hi!", font=font, **kwargs)
|
||||
txt = io.getvalue()
|
||||
return "Font" in txt and "not in" in txt
|
||||
|
||||
# check for normal fonts (no warning)
|
||||
assert not warning_printed("System-ui", warn_missing_font=True)
|
||||
# should be converted to sans before checking
|
||||
assert not warning_printed("Sans-serif", warn_missing_font=True)
|
||||
|
||||
# check random string (should be warning)
|
||||
assert warning_printed("Manim!" * 3, warn_missing_font=True)
|
||||
|
|
|
|||
|
|
@ -8,17 +8,3 @@ class SquareToCircle(Scene):
|
|||
square = Square()
|
||||
circle = Circle()
|
||||
self.play(Transform(square, circle))
|
||||
|
||||
|
||||
class FunctionLikeTest(Scene):
|
||||
def construct(self):
|
||||
assert "FunctionLike" in globals()
|
||||
a = FunctionLike()
|
||||
self.play(FadeIn(a))
|
||||
|
||||
|
||||
class WithAllTest(Scene):
|
||||
def construct(self):
|
||||
assert "WithAll" in globals()
|
||||
a = WithAll()
|
||||
self.play(FadeIn(a))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ from __future__ import annotations
|
|||
|
||||
import random
|
||||
import string
|
||||
import tempfile
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -142,116 +141,3 @@ def create_plugin(tmp_path, python_version, random_string):
|
|||
out, err, exit_code = capture(command)
|
||||
print(out)
|
||||
assert exit_code == 0, err
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_plugin_function_like(
|
||||
tmp_path,
|
||||
create_plugin,
|
||||
python_version,
|
||||
simple_scenes_path,
|
||||
):
|
||||
function_like_plugin = create_plugin(
|
||||
"{plugin_name}.__init__:import_all",
|
||||
"FunctionLike",
|
||||
"import_all",
|
||||
)
|
||||
cfg_file = cfg_file_create(
|
||||
cfg_file_contents.format(plugin_name=function_like_plugin["plugin_name"]),
|
||||
tmp_path,
|
||||
)
|
||||
scene_name = "FunctionLikeTest"
|
||||
command = [
|
||||
python_version,
|
||||
"-m",
|
||||
"manim",
|
||||
"-ql",
|
||||
"--media_dir",
|
||||
str(cfg_file.parent),
|
||||
"--config_file",
|
||||
str(cfg_file),
|
||||
str(simple_scenes_path),
|
||||
scene_name,
|
||||
]
|
||||
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
|
||||
print(out)
|
||||
print(err)
|
||||
assert exit_code == 0, err
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_plugin_no_all(tmp_path, create_plugin, python_version):
|
||||
create_plugin = create_plugin("{plugin_name}", "NoAll", "import_all")
|
||||
plugin_name = create_plugin["plugin_name"]
|
||||
cfg_file = cfg_file_create(
|
||||
cfg_file_contents.format(plugin_name=plugin_name),
|
||||
tmp_path,
|
||||
)
|
||||
test_class = textwrap.dedent(
|
||||
f"""\
|
||||
from manim import *
|
||||
class NoAllTest(Scene):
|
||||
def construct(self):
|
||||
assert "{plugin_name}" in globals()
|
||||
a = {plugin_name}.NoAll()
|
||||
self.play(FadeIn(a))
|
||||
""",
|
||||
)
|
||||
|
||||
with tempfile.NamedTemporaryFile(
|
||||
mode="w",
|
||||
encoding="utf-8",
|
||||
suffix=".py",
|
||||
delete=False,
|
||||
) as tmpfile:
|
||||
tmpfile.write(test_class)
|
||||
scene_name = "NoAllTest"
|
||||
command = [
|
||||
python_version,
|
||||
"-m",
|
||||
"manim",
|
||||
"-ql",
|
||||
"--media_dir",
|
||||
str(cfg_file.parent),
|
||||
"--config_file",
|
||||
str(cfg_file),
|
||||
tmpfile.name,
|
||||
scene_name,
|
||||
]
|
||||
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
|
||||
print(out)
|
||||
print(err)
|
||||
assert exit_code == 0, err
|
||||
Path(tmpfile.name).unlink()
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_plugin_with_all(tmp_path, create_plugin, python_version, simple_scenes_path):
|
||||
create_plugin = create_plugin(
|
||||
"{plugin_name}",
|
||||
"WithAll",
|
||||
"import_all",
|
||||
all_dec="__all__=['WithAll']",
|
||||
)
|
||||
plugin_name = create_plugin["plugin_name"]
|
||||
cfg_file = cfg_file_create(
|
||||
cfg_file_contents.format(plugin_name=plugin_name),
|
||||
tmp_path,
|
||||
)
|
||||
scene_name = "WithAllTest"
|
||||
command = [
|
||||
python_version,
|
||||
"-m",
|
||||
"manim",
|
||||
"-ql",
|
||||
"--media_dir",
|
||||
str(cfg_file.parent),
|
||||
"--config_file",
|
||||
str(cfg_file),
|
||||
str(simple_scenes_path),
|
||||
scene_name,
|
||||
]
|
||||
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
|
||||
print(out)
|
||||
print(err)
|
||||
assert exit_code == 0, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue