diff --git a/.flake8 b/.flake8 index 15e385901c..b16c3bf625 100644 --- a/.flake8 +++ b/.flake8 @@ -2,7 +2,7 @@ # Exclude the grpc generated code exclude = ./manim/grpc/gen/*, __pycache__,.git, per-file-ignores = __init__.py:F401 -max-complexity = 15 +max-complexity = 29 max-line-length = 88 statistics = True # Prevents some flake8-rst-docstrings errors @@ -27,9 +27,6 @@ extend-ignore = E203, W503, D202, D212, D213, D404 # Plug-in: flake8-simplify SIM105, SIM106, SIM119, - # Plug-in: flake8-comprehensions - C901 - # Plug-in: flake8-pytest-style PT001, PT004, PT006, PT011, PT018, PT022, PT023, diff --git a/LICENSE.community b/LICENSE.community index 8e35bc50b2..c1c149f1eb 100644 --- a/LICENSE.community +++ b/LICENSE.community @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021, the Manim Community Developers +Copyright (c) 2024, the Manim Community Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/docs/source/contributing/development.rst b/docs/source/contributing/development.rst index 03de21642d..d3ef8fadf5 100644 --- a/docs/source/contributing/development.rst +++ b/docs/source/contributing/development.rst @@ -62,7 +62,7 @@ For first-time contributors managing virtual environments. If you choose to use Poetry as well, follow `Poetry's installation - guidelines `__ + guidelines `__ to install it on your system, then run ``poetry install`` from your cloned repository. Poetry will then install Manim, as well as create and enter a virtual environment. You can always re-enter diff --git a/docs/source/contributing/docs/typings.rst b/docs/source/contributing/docs/typings.rst index 748cff1e02..72891b0298 100644 --- a/docs/source/contributing/docs/typings.rst +++ b/docs/source/contributing/docs/typings.rst @@ -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: ----------------------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 8e6d8c2fb6..7afffef4d3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -30,7 +30,7 @@ in the right place! You can also find information on Manim's docker images and (online) notebook environments there. - Want to try the library before installing it? Take a look at our - interactive online playground at https://try.manim.community in form + interactive online playground at https://try.manim.community in the form of a Jupyter notebook. - In our :doc:`Tutorials ` section you will find a collection of resources that will teach you how to use Manim. In particular, @@ -71,7 +71,7 @@ Here are some short summaries for all of the sections in this documentation: can be found in the :doc:`FAQ ` section. - The :doc:`Reference Manual ` contains a comprehensive list of all of Manim's (documented) modules, classes, and functions. If you are somewhat familiar with Manim's - module structure feel free to browse the manual directly. If you are searching for + module structure, feel free to browse the manual directly. If you are searching for something specific, feel free to use the documentation's search feature in the sidebar. Many classes and methods come with their own illustrated examples too! - The :doc:`Plugins ` page documents how to install, write, and distribute diff --git a/docs/source/installation/jupyter.rst b/docs/source/installation/jupyter.rst index 3b9942e5ec..7542e59be7 100644 --- a/docs/source/installation/jupyter.rst +++ b/docs/source/installation/jupyter.rst @@ -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 diff --git a/docs/source/plugins.rst b/docs/source/plugins.rst index 0359d29b5e..c4c631b655 100644 --- a/docs/source/plugins.rst +++ b/docs/source/plugins.rst @@ -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 `_ 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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/manim/__main__.py b/manim/__main__.py index c11ca88450..f05f4c2971 100644 --- a/manim/__main__.py +++ b/manim/__main__.py @@ -1,7 +1,5 @@ from __future__ import annotations -import sys - import click import cloup diff --git a/manim/animation/indication.py b/manim/animation/indication.py index 90d8113b43..01a0e619a5 100644 --- a/manim/animation/indication.py +++ b/manim/animation/indication.py @@ -31,7 +31,6 @@ def construct(self): "Flash", "ShowPassingFlash", "ShowPassingFlashWithThinningStrokeWidth", - "ShowCreationThenFadeOut", "ApplyWave", "Circumscribe", "Wiggle", @@ -148,7 +147,7 @@ def construct(self): 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 +157,7 @@ def __init__( 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) @@ -342,16 +341,6 @@ def __init__(self, vmobject, n_segments=10, time_width=0.1, remover=True, **kwar ) -@deprecated( - since="v0.15.0", - until="v0.16.0", - message="Use Create then FadeOut to achieve this effect.", -) -class ShowCreationThenFadeOut(Succession): - def __init__(self, mobject: "Mobject", remover: bool = True, **kwargs) -> None: - super().__init__(Create(mobject), FadeOut(mobject), remover=remover, **kwargs) - - class ApplyWave(Homotopy): """Send a wave through the Mobject distorting it temporarily. @@ -397,7 +386,7 @@ def construct(self): def __init__( self, - mobject: "Mobject", + mobject: Mobject, direction: np.ndarray = UP, amplitude: float = 0.2, wave_func: Callable[[float], float] = smooth, @@ -516,7 +505,7 @@ def construct(self): def __init__( self, - mobject: "Mobject", + mobject: Mobject, scale_value: float = 1.1, rotation_angle: float = 0.01 * TAU, n_wiggles: int = 6, @@ -544,8 +533,8 @@ def get_rotate_about_point(self) -> np.ndarray: def interpolate_submobject( self, - submobject: "Mobject", - starting_submobject: "Mobject", + submobject: Mobject, + starting_submobject: Mobject, alpha: float, ) -> None: submobject.points[:, :] = starting_submobject.points diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index 93b0d73bf0..2989dcb44a 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -17,7 +17,6 @@ from typing import TYPE_CHECKING import numpy as np -from typing_extensions import Self from manim import config from manim.constants import * @@ -31,6 +30,8 @@ from manim.utils.space_ops import angle_of_vector, line_intersection, normalize if TYPE_CHECKING: + from typing_extensions import Self + from manim.typing import Point2D, Point3D, Vector3D from manim.utils.color import ParsableManimColor @@ -659,7 +660,9 @@ def construct(self): self.add(plane, vector_1, vector_2) """ - def __init__(self, direction: Vector3D = RIGHT, buff: float = 0, **kwargs) -> None: + def __init__( + self, direction: Point2D | Point3D = RIGHT, buff: float = 0, **kwargs + ) -> None: self.buff = buff if len(direction) == 2: direction = np.hstack([direction, 0]) diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 1b5eed0d58..ce5c971192 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -882,7 +882,7 @@ def has_time_based_updater(self) -> bool: Returns ------- - class:`bool` + :class:`bool` ``True`` if at least one updater uses the ``dt`` parameter, ``False`` otherwise. @@ -1905,7 +1905,17 @@ def fade(self, darkness: float = 0.5, family: bool = True) -> Self: return self def get_color(self) -> ManimColor: - """Returns the color of the :class:`~.Mobject`""" + """Returns the color of the :class:`~.Mobject` + + Examples + -------- + :: + + >>> from manim import Square, RED + >>> Square(color=RED).get_color() == RED + True + + """ return self.color ## @@ -2700,13 +2710,13 @@ def push_self_into_submobjects(self) -> Self: def add_n_more_submobjects(self, n: int) -> Self | None: if n == 0: - return + return None curr = len(self.submobjects) if curr == 0: # If empty, simply add n point mobjects self.submobjects = [self.get_point_mobject() for k in range(n)] - return + return None target = curr + n # TODO, factor this out to utils so as to reuse @@ -2761,7 +2771,6 @@ def interpolate_color(self, mobject1: Mobject, mobject2: Mobject, alpha: float): def become( self, mobject: Mobject, - copy_submobjects: bool = True, match_height: bool = False, match_width: bool = False, match_depth: bool = False, @@ -2774,20 +2783,25 @@ def become( .. note:: If both match_height and match_width are ``True`` then the transformed :class:`~.Mobject` - will match the height first and then the width + will match the height first and then the width. Parameters ---------- match_height - If ``True``, then the transformed :class:`~.Mobject` will match the height of the original + Whether or not to preserve the height of the original + :class:`~.Mobject`. match_width - If ``True``, then the transformed :class:`~.Mobject` will match the width of the original + Whether or not to preserve the width of the original + :class:`~.Mobject`. match_depth - If ``True``, then the transformed :class:`~.Mobject` will match the depth of the original + Whether or not to preserve the depth of the original + :class:`~.Mobject`. match_center - If ``True``, then the transformed :class:`~.Mobject` will match the center of the original + Whether or not to preserve the center of the original + :class:`~.Mobject`. stretch - If ``True``, then the transformed :class:`~.Mobject` will stretch to fit the proportions of the original + Whether or not to stretch the target mobject to match the + the proportions of the original :class:`~.Mobject`. Examples -------- @@ -2801,8 +2815,62 @@ def construct(self): self.wait(0.5) circ.become(square) self.wait(0.5) - """ + + The following examples illustrate how mobject measurements + change when using the ``match_...`` and ``stretch`` arguments. + We start with a rectangle that is 2 units high and 4 units wide, + which we want to turn into a circle of radius 3:: + + >>> from manim import Rectangle, Circle + >>> import numpy as np + >>> rect = Rectangle(height=2, width=4) + >>> circ = Circle(radius=3) + + With ``stretch=True``, the target circle is deformed to match + the proportions of the rectangle, which results in the target + mobject being an ellipse with height 2 and width 4. We can + check that the resulting points satisfy the ellipse equation + :math:`x^2/a^2 + y^2/b^2 = 1` with :math:`a = 4/2` and :math:`b = 2/2` + being the semi-axes:: + + >>> result = rect.copy().become(circ, stretch=True) + >>> result.height, result.width + (2.0, 4.0) + >>> ellipse_eq = np.sum(result.get_anchors()**2 * [1/4, 1, 0], axis=1) + >>> np.allclose(ellipse_eq, 1) + True + + With ``match_height=True`` and ``match_width=True`` the circle is + scaled such that the height or the width of the rectangle will + be preserved, respectively. + The points of the resulting mobject satisfy the circle equation + :math:`x^2 + y^2 = r^2` for the corresponding radius :math:`r`:: + + >>> result = rect.copy().become(circ, match_height=True) + >>> result.height, result.width + (2.0, 2.0) + >>> circle_eq = np.sum(result.get_anchors()**2, axis=1) + >>> np.allclose(circle_eq, 1) + True + >>> result = rect.copy().become(circ, match_width=True) + >>> result.height, result.width + (4.0, 4.0) + >>> circle_eq = np.sum(result.get_anchors()**2, axis=1) + >>> np.allclose(circle_eq, 2**2) + True + + With ``match_center=True``, the resulting mobject is moved such that + its center is the same as the center of the original mobject:: + + >>> rect = rect.shift(np.array([0, 1, 0])) + >>> np.allclose(rect.get_center(), circ.get_center()) + False + >>> result = rect.copy().become(circ, match_center=True) + >>> np.allclose(rect.get_center(), result.get_center()) + True + """ + mobject = mobject.copy() if stretch: mobject.stretch_to_fit_height(self.height) mobject.stretch_to_fit_width(self.width) diff --git a/manim/mobject/opengl/opengl_geometry.py b/manim/mobject/opengl/opengl_geometry.py index 3e81f6f6f3..2ec0bbe4dd 100644 --- a/manim/mobject/opengl/opengl_geometry.py +++ b/manim/mobject/opengl/opengl_geometry.py @@ -23,7 +23,6 @@ ) DEFAULT_DOT_RADIUS = 0.08 -DEFAULT_SMALL_DOT_RADIUS = 0.04 DEFAULT_DASH_LENGTH = 0.05 DEFAULT_ARROW_TIP_LENGTH = 0.35 DEFAULT_ARROW_TIP_WIDTH = 0.35 diff --git a/manim/mobject/svg/svg_mobject.py b/manim/mobject/svg/svg_mobject.py index 87cad3bda0..c029daa942 100644 --- a/manim/mobject/svg/svg_mobject.py +++ b/manim/mobject/svg/svg_mobject.py @@ -510,17 +510,15 @@ def handle_commands(self) -> None: all_points: list[np.ndarray] = [] last_move = None curve_start = None + last_true_move = None - # These lambdas behave the same as similar functions in - # vectorized_mobject, except they add to a list of points instead - # of updating this Mobject's numpy array of points. This way, - # we don't observe O(n^2) behavior for complex paths due to - # numpy's need to re-allocate memory on every append. - def move_pen(pt): - nonlocal last_move, curve_start + def move_pen(pt, *, true_move: bool = False): + nonlocal last_move, curve_start, last_true_move last_move = pt if curve_start is None: curve_start = last_move + if true_move: + last_true_move = last_move if self.n_points_per_curve == 4: @@ -568,7 +566,7 @@ def add_line(start, end): for segment in self.path_obj: segment_class = segment.__class__ if segment_class == se.Move: - move_pen(_convert_point_to_3d(*segment.end)) + move_pen(_convert_point_to_3d(*segment.end), true_move=True) elif segment_class == se.Line: add_line(last_move, _convert_point_to_3d(*segment.end)) elif segment_class == se.QuadraticBezier: @@ -588,8 +586,8 @@ def add_line(start, end): # If the SVG path naturally ends at the beginning of the curve, # we do *not* need to draw a closing line. To account for floating # point precision, we use a small value to compare the two points. - if abs(np.linalg.norm(last_move - curve_start)) > 0.0001: - add_line(last_move, curve_start) + if abs(np.linalg.norm(last_move - last_true_move)) > 0.0001: + add_line(last_move, last_true_move) curve_start = None else: raise AssertionError(f"Not implemented: {segment_class}") diff --git a/manim/mobject/table.py b/manim/mobject/table.py index ff0c0f0bd9..1a1beffad1 100644 --- a/manim/mobject/table.py +++ b/manim/mobject/table.py @@ -74,7 +74,6 @@ def construct(self): from manim.mobject.text.tex_mobject import MathTex from manim.mobject.text.text_mobject import Paragraph -from .. import config from ..animation.animation import Animation from ..animation.composition import AnimationGroup from ..animation.creation import Create, Write diff --git a/manim/mobject/text/tex_mobject.py b/manim/mobject/text/tex_mobject.py index 47cbe6cd96..029b5ce009 100644 --- a/manim/mobject/text/tex_mobject.py +++ b/manim/mobject/text/tex_mobject.py @@ -34,7 +34,7 @@ from manim.constants import * from manim.mobject.geometry.line import Line from manim.mobject.svg.svg_mobject import SVGMobject -from manim.mobject.types.vectorized_mobject import VectorizedPoint, VGroup, VMobject +from manim.mobject.types.vectorized_mobject import VGroup, VMobject from manim.utils.tex import TexTemplate from manim.utils.tex_file_writing import tex_to_svg_file diff --git a/manim/mobject/text/text_mobject.py b/manim/mobject/text/text_mobject.py index 32f5e4dc25..d394bccd23 100644 --- a/manim/mobject/text/text_mobject.py +++ b/manim/mobject/text/text_mobject.py @@ -444,8 +444,21 @@ def __init__( **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 @@ def __init__( ) -> 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 diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 8312b74459..5e10fadc64 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -26,20 +26,17 @@ ) import numpy as np -import numpy.typing as npt from PIL.Image import Image -from typing_extensions import Self +from manim import config +from manim.constants import * +from manim.mobject.mobject import Mobject from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject from manim.mobject.three_d.three_d_utils import ( get_3d_vmob_gradient_start_and_end_points, ) - -from ... import config -from ...constants import * -from ...mobject.mobject import Mobject -from ...utils.bezier import ( +from manim.utils.bezier import ( bezier, get_smooth_handle_points, integer_interpolate, @@ -47,11 +44,19 @@ partial_bezier_points, proportions_along_bezier_curve_for_point, ) -from ...utils.color import BLACK, WHITE, ManimColor, ParsableManimColor -from ...utils.iterables import make_even, resize_array, stretch_array_to_length, tuplify -from ...utils.space_ops import rotate_vector, shoelace_direction +from manim.utils.color import BLACK, WHITE, ManimColor, ParsableManimColor +from manim.utils.iterables import ( + make_even, + resize_array, + stretch_array_to_length, + tuplify, +) +from manim.utils.space_ops import rotate_vector, shoelace_direction if TYPE_CHECKING: + import numpy.typing as npt + from typing_extensions import Self + from manim.typing import ( BezierPoints, CubicBezierPoints, @@ -347,7 +352,7 @@ def set_stroke( setattr(self, opacity_name, opacity) if color is not None and background: if isinstance(color, (list, tuple)): - self.background_stroke_color = color + self.background_stroke_color = ManimColor.parse(color) else: self.background_stroke_color = ManimColor(color) return self @@ -957,6 +962,27 @@ def set_points_as_corners(self, points: Point3D_Array) -> Self: ------- :class:`VMobject` ``self`` + + + Examples + -------- + .. manim:: PointsAsCornersExample + :save_last_frame: + + class PointsAsCornersExample(Scene): + def construct(self): + corners = ( + # create square + UR, UL, + DL, DR, + UR, + # create crosses + DL, UL, + DR + ) + vmob = VMobject(stroke_color=RED) + vmob.set_points_as_corners(corners).scale(2) + self.add(vmob) """ nppcc = self.n_points_per_cubic_curve points = np.array(points) @@ -1382,6 +1408,22 @@ def point_from_proportion(self, alpha: float) -> Point3D: If ``alpha`` is not between 0 and 1. :exc:`Exception` If the :class:`VMobject` has no points. + + Example + ------- + .. manim:: PointFromProportion + :save_last_frame: + + class PointFromProportion(Scene): + def construct(self): + line = Line(2*DL, 2*UR) + self.add(line) + colors = (RED, BLUE, YELLOW) + proportions = (1/4, 1/2, 3/4) + for color, proportion in zip(colors, proportions): + self.add(Dot(color=color).move_to( + line.point_from_proportion(proportion) + )) """ if alpha < 0 or alpha > 1: @@ -1406,6 +1448,9 @@ def point_from_proportion(self, alpha: float) -> Point3D: return curve(residue) current_length += length + raise Exception( + "Not sure how you reached here, please file a bug report at https://github.com/ManimCommunity/manim/issues/new/choose" + ) def proportion_from_point( self, @@ -1730,7 +1775,10 @@ def interpolate_color( interpolate(getattr(mobject1, attr), getattr(mobject2, attr), alpha), ) if alpha == 1.0: - setattr(self, attr, getattr(mobject2, attr)) + val = getattr(mobject2, attr) + if isinstance(val, np.ndarray): + val = val.copy() + setattr(self, attr, val) def pointwise_become_partial( self, diff --git a/manim/plugins/__init__.py b/manim/plugins/__init__.py index a899f63bb8..06314895e1 100644 --- a/manim/plugins/__init__.py +++ b/manim/plugins/__init__.py @@ -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) diff --git a/manim/plugins/import_plugins.py b/manim/plugins/import_plugins.py deleted file mode 100644 index 1f11accbf9..0000000000 --- a/manim/plugins/import_plugins.py +++ /dev/null @@ -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) diff --git a/manim/plugins/plugins_flags.py b/manim/plugins/plugins_flags.py index a1487e5774..3733ac3f3f 100644 --- a/manim/plugins/plugins_flags.py +++ b/manim/plugins/plugins_flags.py @@ -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() diff --git a/manim/renderer/opengl_renderer.py b/manim/renderer/opengl_renderer.py index 79390be97c..d457682267 100644 --- a/manim/renderer/opengl_renderer.py +++ b/manim/renderer/opengl_renderer.py @@ -219,9 +219,6 @@ def interpolate(self, *args, **kwargs): self.refresh_rotation_matrix() -points_per_curve = 3 - - class OpenGLRenderer: def __init__(self, file_writer_class=SceneFileWriter, skip_animations=False): # Measured in pixel widths, used for vector graphics diff --git a/manim/typing.py b/manim/typing.py index 9d8bd7a4c5..8111ca7398 100644 --- a/manim/typing.py +++ b/manim/typing.py @@ -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 @@ -319,7 +319,7 @@ Vector types """ -Vector2D: TypeAlias = Point2D +Vector2D: TypeAlias = npt.NDArray[PointDType] """``shape: (2,)`` A 2-dimensional vector: ``[float, float]``. @@ -332,7 +332,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 diff --git a/manim/utils/paths.py b/manim/utils/paths.py index 9d7ec10d73..4cf867b428 100644 --- a/manim/utils/paths.py +++ b/manim/utils/paths.py @@ -10,28 +10,22 @@ ] -from typing import Callable +from typing import TYPE_CHECKING import numpy as np from ..constants import OUT from ..utils.bezier import interpolate -from ..utils.deprecation import deprecated_params from ..utils.space_ops import rotation_matrix -STRAIGHT_PATH_THRESHOLD = 0.01 +if TYPE_CHECKING: + from manim.typing import PathFuncType, Vector3D + -PATH_FUNC_TYPE = Callable[[np.ndarray, np.ndarray, float], np.ndarray] +STRAIGHT_PATH_THRESHOLD = 0.01 -# Remove `*args` and the `if` inside the functions when removing deprecation -@deprecated_params( - params="start_points, end_points, alpha", - since="v0.14", - until="v0.15", - message="Straight path is now returning interpolating function to make it consistent with other path functions. Use straight_path()(a,b,c) instead of straight_path(a,b,c).", -) -def straight_path(*args) -> PATH_FUNC_TYPE: +def straight_path(): """Simplest path function. Each point in a set goes in a straight path toward its destination. Examples @@ -74,14 +68,12 @@ def construct(self): self.wait() """ - if len(args) > 0: - return interpolate(*args) return interpolate def path_along_circles( - arc_angle: float, circles_centers: np.ndarray, axis: np.ndarray = OUT -) -> PATH_FUNC_TYPE: + arc_angle: float, circles_centers: np.ndarray, axis: Vector3D = OUT +) -> PathFuncType: """This function transforms each point by moving it roughly along a circle, each with its own specified center. The path may be seen as each point smoothly changing its orbit from its starting position to its destination. @@ -158,7 +150,7 @@ def path(start_points: np.ndarray, end_points: np.ndarray, alpha: float): return path -def path_along_arc(arc_angle: float, axis: np.ndarray = OUT) -> PATH_FUNC_TYPE: +def path_along_arc(arc_angle: float, axis: Vector3D = OUT) -> PathFuncType: """This function transforms each point by moving it along a circular arc. Parameters @@ -225,7 +217,7 @@ def path(start_points: np.ndarray, end_points: np.ndarray, alpha: float): return path -def clockwise_path() -> PATH_FUNC_TYPE: +def clockwise_path() -> PathFuncType: """This function transforms each point by moving clockwise around a half circle. Examples @@ -271,7 +263,7 @@ def construct(self): return path_along_arc(-np.pi) -def counterclockwise_path() -> PATH_FUNC_TYPE: +def counterclockwise_path() -> PathFuncType: """This function transforms each point by moving counterclockwise around a half circle. Examples @@ -317,7 +309,7 @@ def construct(self): return path_along_arc(np.pi) -def spiral_path(angle: float, axis: np.ndarray = OUT) -> PATH_FUNC_TYPE: +def spiral_path(angle: float, axis: Vector3D = OUT) -> PathFuncType: """This function transforms each point by moving along a spiral to its destination. Parameters diff --git a/poetry.lock b/poetry.lock index b5fd7d925a..85ce76e191 100644 --- a/poetry.lock +++ b/poetry.lock @@ -219,33 +219,33 @@ lxml = ["lxml"] [[package]] name = "black" -version = "23.12.1" +version = "24.3.0" description = "The uncompromising code formatter." optional = false python-versions = ">=3.8" files = [ - {file = "black-23.12.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2"}, - {file = "black-23.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba"}, - {file = "black-23.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0"}, - {file = "black-23.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3"}, - {file = "black-23.12.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba"}, - {file = "black-23.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b"}, - {file = "black-23.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59"}, - {file = "black-23.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50"}, - {file = "black-23.12.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e"}, - {file = "black-23.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec"}, - {file = "black-23.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e"}, - {file = "black-23.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9"}, - {file = "black-23.12.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f"}, - {file = "black-23.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d"}, - {file = "black-23.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a"}, - {file = "black-23.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e"}, - {file = "black-23.12.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055"}, - {file = "black-23.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54"}, - {file = "black-23.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea"}, - {file = "black-23.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2"}, - {file = "black-23.12.1-py3-none-any.whl", hash = "sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e"}, - {file = "black-23.12.1.tar.gz", hash = "sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5"}, + {file = "black-24.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395"}, + {file = "black-24.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995"}, + {file = "black-24.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"}, + {file = "black-24.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0"}, + {file = "black-24.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9"}, + {file = "black-24.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597"}, + {file = "black-24.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d"}, + {file = "black-24.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5"}, + {file = "black-24.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f"}, + {file = "black-24.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11"}, + {file = "black-24.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4"}, + {file = "black-24.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5"}, + {file = "black-24.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837"}, + {file = "black-24.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd"}, + {file = "black-24.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213"}, + {file = "black-24.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959"}, + {file = "black-24.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb"}, + {file = "black-24.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7"}, + {file = "black-24.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7"}, + {file = "black-24.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f"}, + {file = "black-24.3.0-py3-none-any.whl", hash = "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93"}, + {file = "black-24.3.0.tar.gz", hash = "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f"}, ] [package.dependencies] @@ -654,43 +654,43 @@ toml = ["tomli"] [[package]] name = "cryptography" -version = "42.0.0" +version = "42.0.4" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:c640b0ef54138fde761ec99a6c7dc4ce05e80420262c20fa239e694ca371d434"}, - {file = "cryptography-42.0.0-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:678cfa0d1e72ef41d48993a7be75a76b0725d29b820ff3cfd606a5b2b33fda01"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:146e971e92a6dd042214b537a726c9750496128453146ab0ee8971a0299dc9bd"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87086eae86a700307b544625e3ba11cc600c3c0ef8ab97b0fda0705d6db3d4e3"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0a68bfcf57a6887818307600c3c0ebc3f62fbb6ccad2240aa21887cda1f8df1b"}, - {file = "cryptography-42.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:5a217bca51f3b91971400890905a9323ad805838ca3fa1e202a01844f485ee87"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ca20550bb590db16223eb9ccc5852335b48b8f597e2f6f0878bbfd9e7314eb17"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:33588310b5c886dfb87dba5f013b8d27df7ffd31dc753775342a1e5ab139e59d"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9515ea7f596c8092fdc9902627e51b23a75daa2c7815ed5aa8cf4f07469212ec"}, - {file = "cryptography-42.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:35cf6ed4c38f054478a9df14f03c1169bb14bd98f0b1705751079b25e1cb58bc"}, - {file = "cryptography-42.0.0-cp37-abi3-win32.whl", hash = "sha256:8814722cffcfd1fbd91edd9f3451b88a8f26a5fd41b28c1c9193949d1c689dc4"}, - {file = "cryptography-42.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:a2a8d873667e4fd2f34aedab02ba500b824692c6542e017075a2efc38f60a4c0"}, - {file = "cryptography-42.0.0-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:8fedec73d590fd30c4e3f0d0f4bc961aeca8390c72f3eaa1a0874d180e868ddf"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be41b0c7366e5549265adf2145135dca107718fa44b6e418dc7499cfff6b4689"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ca482ea80626048975360c8e62be3ceb0f11803180b73163acd24bf014133a0"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c58115384bdcfe9c7f644c72f10f6f42bed7cf59f7b52fe1bf7ae0a622b3a139"}, - {file = "cryptography-42.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:56ce0c106d5c3fec1038c3cca3d55ac320a5be1b44bf15116732d0bc716979a2"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:324721d93b998cb7367f1e6897370644751e5580ff9b370c0a50dc60a2003513"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:d97aae66b7de41cdf5b12087b5509e4e9805ed6f562406dfcf60e8481a9a28f8"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:85f759ed59ffd1d0baad296e72780aa62ff8a71f94dc1ab340386a1207d0ea81"}, - {file = "cryptography-42.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:206aaf42e031b93f86ad60f9f5d9da1b09164f25488238ac1dc488334eb5e221"}, - {file = "cryptography-42.0.0-cp39-abi3-win32.whl", hash = "sha256:74f18a4c8ca04134d2052a140322002fef535c99cdbc2a6afc18a8024d5c9d5b"}, - {file = "cryptography-42.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:14e4b909373bc5bf1095311fa0f7fcabf2d1a160ca13f1e9e467be1ac4cbdf94"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3005166a39b70c8b94455fdbe78d87a444da31ff70de3331cdec2c568cf25b7e"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:be14b31eb3a293fc6e6aa2807c8a3224c71426f7c4e3639ccf1a2f3ffd6df8c3"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:bd7cf7a8d9f34cc67220f1195884151426ce616fdc8285df9054bfa10135925f"}, - {file = "cryptography-42.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c310767268d88803b653fffe6d6f2f17bb9d49ffceb8d70aed50ad45ea49ab08"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bdce70e562c69bb089523e75ef1d9625b7417c6297a76ac27b1b8b1eb51b7d0f"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:e9326ca78111e4c645f7e49cbce4ed2f3f85e17b61a563328c85a5208cf34440"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:69fd009a325cad6fbfd5b04c711a4da563c6c4854fc4c9544bff3088387c77c0"}, - {file = "cryptography-42.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:988b738f56c665366b1e4bfd9045c3efae89ee366ca3839cd5af53eaa1401bce"}, - {file = "cryptography-42.0.0.tar.gz", hash = "sha256:6cf9b76d6e93c62114bd19485e5cb003115c134cf9ce91f8ac924c44f8c8c3f4"}, + {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449"}, + {file = "cryptography-42.0.4-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b"}, + {file = "cryptography-42.0.4-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824"}, + {file = "cryptography-42.0.4-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b"}, + {file = "cryptography-42.0.4-cp37-abi3-win32.whl", hash = "sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925"}, + {file = "cryptography-42.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923"}, + {file = "cryptography-42.0.4-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9"}, + {file = "cryptography-42.0.4-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929"}, + {file = "cryptography-42.0.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0"}, + {file = "cryptography-42.0.4-cp39-abi3-win32.whl", hash = "sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129"}, + {file = "cryptography-42.0.4-cp39-abi3-win_amd64.whl", hash = "sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20"}, + {file = "cryptography-42.0.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b"}, + {file = "cryptography-42.0.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660"}, + {file = "cryptography-42.0.4.tar.gz", hash = "sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb"}, ] [package.dependencies] @@ -4533,4 +4533,4 @@ jupyterlab = ["jupyterlab", "notebook"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "f15fa632919381a9b5b2cebc3e89aa307fa8735db2e5cde7a408765b46a3b00f" +content-hash = "36d6cb5963f1e9a0ab3d646de64b016ff2afeb6a9ed770188ce04ffadf030bbc" diff --git a/pyproject.toml b/pyproject.toml index 1ba36a47b1..7590a73adf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 @@ -60,7 +61,7 @@ jupyterlab = ["jupyterlab", "notebook"] gui = ["dearpygui"] [tool.poetry.group.dev.dependencies] -black = "^23.11.0" +black = ">=23.11,<25.0" flake8 = "^6.1.0" flake8-bugbear = "^23.11.28" flake8-builtins = "^2.2.0" diff --git a/tests/module/mobject/svg/test_svg_mobject.py b/tests/module/mobject/svg/test_svg_mobject.py index eba36fb3ab..1d8f6190ef 100644 --- a/tests/module/mobject/svg/test_svg_mobject.py +++ b/tests/module/mobject/svg/test_svg_mobject.py @@ -134,3 +134,71 @@ def test_closed_path_does_not_have_extra_point(): ), decimal=5, ) + + +def test_close_command_closes_last_move_not_the_starting_one(): + # This A.svg is the output of a Text("A") in some systems + # It contains a path that moves from the outer boundary of the A + # to the boundary of the inner triangle, anc then closes the path + # which should close the inner triangle and not the outer boundary. + svg = SVGMobject( + get_svg_resource("A.svg"), + ) + assert len(svg.points) == 0, svg.points + assert len(svg.submobjects) == 1, svg.submobjects + capital_A = svg.submobjects[0] + + # The last point should not be the same as the first point + assert not all(capital_A.points[0] == capital_A.points[-1]) + np.testing.assert_almost_equal( + capital_A.points, + np.array( + [ + [-0.8380339075214888, -1.0, 1.2246467991473532e-16], + [-0.6132152047642527, -0.3333333333333336, 4.082155997157847e-17], + [-0.388396502007016, 0.3333333333333336, -4.082155997157847e-17], + [-0.16357779924977994, 1.0, -1.2246467991473532e-16], + [-0.16357779924977994, 1.0, -1.2246467991473532e-16], + [-0.05425733591657368, 1.0, -1.2246467991473532e-16], + [0.05506312741663405, 1.0, -1.2246467991473532e-16], + [0.16438359074984032, 1.0, -1.2246467991473532e-16], + [0.16438359074984032, 1.0, -1.2246467991473532e-16], + [0.3889336963403905, 0.3333333333333336, -4.082155997157847e-17], + [0.6134838019309422, -0.3333333333333336, 4.082155997157847e-17], + [0.8380339075214923, -1.0, 1.2246467991473532e-16], + [0.8380339075214923, -1.0, 1.2246467991473532e-16], + [0.744560897060354, -1.0, 1.2246467991473532e-16], + [0.6510878865992157, -1.0, 1.2246467991473532e-16], + [0.5576148761380774, -1.0, 1.2246467991473532e-16], + [0.5576148761380774, -1.0, 1.2246467991473532e-16], + [0.49717968849274957, -0.8138597980824822, 9.966907966764229e-17], + [0.4367445008474217, -0.6277195961649644, 7.687347942054928e-17], + [0.3763093132020939, -0.4415793942474466, 5.407787917345625e-17], + [0.3763093132020939, -0.4415793942474466, 5.407787917345625e-17], + [0.12167600863867864, -0.4415793942474466, 5.407787917345625e-17], + [-0.13295729592473662, -0.4415793942474466, 5.407787917345625e-17], + [-0.38759060048815186, -0.4415793942474466, 5.407787917345625e-17], + [-0.38759060048815186, -0.4415793942474466, 5.407787917345625e-17], + [-0.4480257881334797, -0.6277195961649644, 7.687347942054928e-17], + [-0.5084609757788076, -0.8138597980824822, 9.966907966764229e-17], + [-0.5688961634241354, -1.0, 1.2246467991473532e-16], + [-0.5688961634241354, -1.0, 1.2246467991473532e-16], + [-0.6586087447899202, -1.0, 1.2246467991473532e-16], + [-0.7483213261557048, -1.0, 1.2246467991473532e-16], + [-0.8380339075214888, -1.0, 1.2246467991473532e-16], + [0.3021757525699033, -0.21434317946653003, 2.6249468865275272e-17], + [0.1993017037512583, 0.09991949373745423, -1.2236608817799732e-17], + [0.09642765493261184, 0.4141821669414385, -5.072268650087473e-17], + [-0.006446393886033166, 0.7284448401454228, -8.920876418394973e-17], + [-0.006446393886033166, 0.7284448401454228, -8.920876418394973e-17], + [-0.10905185929034443, 0.4141821669414385, -5.072268650087473e-17], + [-0.2116573246946542, 0.09991949373745423, -1.2236608817799732e-17], + [-0.31426279009896546, -0.21434317946653003, 2.6249468865275272e-17], + [-0.31426279009896546, -0.21434317946653003, 2.6249468865275272e-17], + [-0.10878327587600921, -0.21434317946653003, 2.6249468865275272e-17], + [0.09669623834694704, -0.21434317946653003, 2.6249468865275272e-17], + [0.3021757525699033, -0.21434317946653003, 2.6249468865275272e-17], + ] + ), + decimal=5, + ) diff --git a/tests/module/mobject/text/test_text_mobject.py b/tests/module/mobject/text/test_text_mobject.py index 048ae883a1..a6b8b3b453 100644 --- a/tests/module/mobject/text/test_text_mobject.py +++ b/tests/module/mobject/text/test_text_mobject.py @@ -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) diff --git a/tests/test_graphical_units/control_data/indication/ShowCreationThenFadeOut.npz b/tests/test_graphical_units/control_data/indication/ShowCreationThenFadeOut.npz deleted file mode 100644 index ecd0ebf1b4..0000000000 Binary files a/tests/test_graphical_units/control_data/indication/ShowCreationThenFadeOut.npz and /dev/null differ diff --git a/tests/test_graphical_units/control_data/mobjects/become.npz b/tests/test_graphical_units/control_data/mobjects/become.npz index afd7ee89db..06b190c3b3 100644 Binary files a/tests/test_graphical_units/control_data/mobjects/become.npz and b/tests/test_graphical_units/control_data/mobjects/become.npz differ diff --git a/tests/test_graphical_units/control_data/mobjects/become_no_color_linking.npz b/tests/test_graphical_units/control_data/mobjects/become_no_color_linking.npz new file mode 100644 index 0000000000..4a0aec2138 Binary files /dev/null and b/tests/test_graphical_units/control_data/mobjects/become_no_color_linking.npz differ diff --git a/tests/test_graphical_units/img_svg_resources/A.svg b/tests/test_graphical_units/img_svg_resources/A.svg new file mode 100644 index 0000000000..b8a0bafab4 --- /dev/null +++ b/tests/test_graphical_units/img_svg_resources/A.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/tests/test_graphical_units/test_indication.py b/tests/test_graphical_units/test_indication.py index 47db2e49a7..592cf5fb38 100644 --- a/tests/test_graphical_units/test_indication.py +++ b/tests/test_graphical_units/test_indication.py @@ -42,13 +42,6 @@ def test_ShowPassingFlash(scene): scene.play(ShowPassingFlash(square.copy())) -@frames_comparison(last_frame=False) -def test_ShowCreationThenFadeOut(scene): - square = Square() - scene.add(square) - scene.play(ShowCreationThenFadeOut(square.copy())) - - @frames_comparison(last_frame=False) def test_ApplyWave(scene): square = Square() diff --git a/tests/test_graphical_units/test_mobjects.py b/tests/test_graphical_units/test_mobjects.py index 88e30cacfa..c76c8599d7 100644 --- a/tests/test_graphical_units/test_mobjects.py +++ b/tests/test_graphical_units/test_mobjects.py @@ -15,18 +15,29 @@ def test_PointCloudDot(scene): @frames_comparison def test_become(scene): s = Rectangle(width=2, height=1, color=RED).shift(UP) - d1, d2, d3 = (Dot() for _ in range(3)) + d = Dot() - s1 = s.copy().become(d1, match_width=True).set_opacity(0.25).set_color(BLUE) + s1 = s.copy().become(d, match_width=True).set_opacity(0.25).set_color(BLUE) s2 = ( s.copy() - .become(d2, match_height=True, match_center=True) + .become(d, match_height=True, match_center=True) .set_opacity(0.25) .set_color(GREEN) ) - s3 = s.copy().become(d3, stretch=True).set_opacity(0.25).set_color(YELLOW) + s3 = s.copy().become(d, stretch=True).set_opacity(0.25).set_color(YELLOW) - scene.add(s, d1, d2, d3, s1, s2, s3) + scene.add(s, d, s1, s2, s3) + + +@frames_comparison +def test_become_no_color_linking(scene): + a = Circle() + b = Square() + scene.add(a) + scene.add(b) + b.become(a) + b.shift(1 * RIGHT) + b.set_stroke(YELLOW, opacity=1) @frames_comparison diff --git a/tests/test_plugins/simple_scenes.py b/tests/test_plugins/simple_scenes.py index 1562196119..e22085123e 100644 --- a/tests/test_plugins/simple_scenes.py +++ b/tests/test_plugins/simple_scenes.py @@ -8,17 +8,3 @@ def construct(self): 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)) diff --git a/tests/test_plugins/test_plugins.py b/tests/test_plugins/test_plugins.py index b73a5d6a1d..e81dc47085 100644 --- a/tests/test_plugins/test_plugins.py +++ b/tests/test_plugins/test_plugins.py @@ -2,7 +2,6 @@ import random import string -import tempfile import textwrap from pathlib import Path @@ -142,116 +141,3 @@ def _create_plugin(entry_point, class_name, function_name, all_dec=""): 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