diff --git a/.flake8 b/.flake8 index 8d4f3309b0..15e385901c 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,7 @@ [flake8] # Exclude the grpc generated code -exclude = ./manim/grpc/gen/* +exclude = ./manim/grpc/gen/*, __pycache__,.git, +per-file-ignores = __init__.py:F401 max-complexity = 15 max-line-length = 88 statistics = True @@ -9,7 +10,7 @@ rst-roles = attr,class,func,meth,mod,obj,ref,doc,exc rst-directives = manim, SEEALSO, seealso docstring-convention=numpy -select = A,A00,B,B9,C4,C90,D,E,F,F,PT,RST,SIM,W +select = A,A00,B,B9,C4,C90,D,E,F,F,PT,RST,SIM,W,F401 # General Compatibility extend-ignore = E203, W503, D202, D212, D213, D404 @@ -40,4 +41,4 @@ extend-ignore = E203, W503, D202, D212, D213, D404 # Plug-in: flake8-rst-docstrings RST201, RST203, RST210, RST212, RST213, RST215, - RST301, RST303, + RST301, RST303, RST499 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 225996766b..aa134edea8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,7 +58,7 @@ repos: flake8-simplify==0.14.1, ] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.4.1 + rev: v1.5.1 hooks: - id: mypy additional_dependencies: @@ -69,6 +69,7 @@ repos: types-requests, types-setuptools, ] + files: ^manim/ - repo: https://github.com/codespell-project/codespell rev: v2.2.5 diff --git a/docs/source/contributing/typings.rst b/docs/source/contributing/typings.rst index 034f4b6faf..9880bfb3b7 100644 --- a/docs/source/contributing/typings.rst +++ b/docs/source/contributing/typings.rst @@ -2,102 +2,80 @@ Adding Typings ============== +.. warning:: + This section is still a work in progress. + Adding type hints to functions and parameters --------------------------------------------- -.. warning:: - This section is still a work in progress. +Manim is currently in the process of adding type hints into the library. In this +section, you will find information about the standards used and some general +guidelines. If you've never used type hints before, this is a good place to get started: https://realpython.com/python-type-checking/#hello-types. -When adding type hints to manim, there are some guidelines that should be followed: +Typing standards +~~~~~~~~~~~~~~~~ -* Coordinates have the typehint ``Sequence[float]``, e.g. +Manim uses `mypy`_ to type check its codebase. You will find a list of +configuration values in the ``mypy.ini`` configuration file. -.. code:: py +To be able to use the newest typing features not available in the lowest +supported Python version, make use of `typing_extensions`_. - def set_points_as_corners(self, points: Sequence[float]) -> "VMobject": - """Given an array of points, set them as corner of the Vmobject.""" +To be able to use the new Union syntax (``|``) and builtins subscripting, use +the ``from __future__ import annotations`` import. -* ``**kwargs`` has no typehint +.. _mypy: https://mypy-lang.org/ +.. _typing_extensions: https://pypi.org/project/typing-extensions/ -* Mobjects have the typehint "Mobject", e.g. +Typing guidelines +~~~~~~~~~~~~~~~~~ -.. code:: py +* Manim has a dedicated :mod:`~.typing` module where type aliases are provided. + Most of them may seem redundant, in particular the ones related to ``numpy``. + This is in anticipation of the support for shape type hinting + (`related issue `_). Besides the + pending shape support, using the correct type aliases will help users understand + which shape should be used. - def match_color(self, mobject: "Mobject"): - """Match the color with the color of another :class:`~.Mobject`.""" - return self.set_color(mobject.get_color()) - -* Colors have the typehint ``Color``, e.g. - -.. code:: py - - def set_color(self, color: Color = YELLOW_C, family: bool = True): - """Condition is function which takes in one arguments, (x, y, z).""" - -* As ``float`` and ``Union[int, float]`` are the same, use only ``float`` - -* For numpy arrays use the typehint ``np.ndarray`` - -* Functions that does not return a value should get the type hint ``None``. (This annotations help catch the kinds of subtle bugs where you are trying to use a meaningless return value. ) +* Always use a type hint of ``None`` for functions that does not return + a value (this also applies to ``__init__``), e.g.: .. code:: py def height(self, value) -> None: self.scale_to_fit_height(value) -* Parameters that are None by default should get the type hint ``Optional`` - -.. code:: py +* For variables representing paths, use the ``StrPath`` or ``StrOrBytesPath`` + type alias defined in the :mod:`~.typing` module. - def rotate( - self, - angle, - axis=OUT, - about_point: Optional[Sequence[float]] = None, - **kwargs, - ): - pass +* ``*args`` and ``**kwargs`` shouldn't be left untyped (in most cases you can + use ``Any``). +* Following `PEP 484 `_, + use ``float`` instead of ``int | float``. -* The ``__init__()`` method always should have None as its return type. - -* Functions and lambda functions should get the typehint ``Callable`` +* Mobjects have the typehint ``Mobject``, e.g.: .. code:: py - rate_func: Callable[[float], float] = lambda t: smooth(1 - t) - - -* Assuming that typical path objects are either Paths or strs, one can use the typehint ``typing.Union[str, pathlib.Path]`` + def match_color(self, mobject: "Mobject"): + """Match the color with the color of another :class:`~.Mobject`.""" + return self.set_color(mobject.get_color()) -.. note:: - As a helper for tool for typesets, you can use `typestring-parser - `_ - which can be accessed by first installing it via ``pip`` - ``pip install typestring-parser`` and - then using ``from typestring_parser import parse``. +* Always parametrize generics (``list[int]`` instead of ``list``, + ``type[Any]`` instead of ``type``, etc.). This also applies to callables: -.. doctest:: - :options: +SKIP +.. code:: py - >>> from typestring_parser import parse - >>> parse("int") - - >>> parse("int or str") - typing.Union[int, str] - >>> parse("list of str or str") - typing.Union[typing.List[str], str] - >>> parse("list of (int, str)") - typing.List[typing.Tuple[int, str]] + rate_func: Callable[[float], float] = lambda t: smooth(1 - t) Missing Sections for typehints are: ----------------------------------- -* Tools for typehinting -* Link to MyPy + * Mypy and numpy import errors: https://realpython.com/python-type-checking/#running-mypy -* Where to find the alias -* When to use Object and when to use "Object". -* The use of a TypeVar on the type hints for copy(). -* The definition and use of Protocols (like Sized, or Sequence, or Iterable...) +* When to use ``object`` vs ``Any`` +* The use of a TypeVar on the type hints for ``copy()``. +* The definition and use of Protocols (like ``Sized``, ``Sequence``, ``Iterable``...) diff --git a/manim/_config/__init__.py b/manim/_config/__init__.py index 94bea642aa..5c8d257293 100644 --- a/manim/_config/__init__.py +++ b/manim/_config/__init__.py @@ -20,7 +20,6 @@ ] parser = make_config_parser() -logger: logging.Logger # The logger can be accessed from anywhere as manim.logger, or as # logging.getLogger("manim"). The console must be accessed as manim.console. diff --git a/manim/_config/logger_utils.py b/manim/_config/logger_utils.py index ca31b4fa54..6ed8751ef2 100644 --- a/manim/_config/logger_utils.py +++ b/manim/_config/logger_utils.py @@ -26,6 +26,7 @@ if TYPE_CHECKING: from pathlib import Path + HIGHLIGHTED_KEYWORDS = [ # these keywords are highlighted specially "Played", "animations", diff --git a/manim/_config/utils.py b/manim/_config/utils.py index a96440ba3b..e7b1d0cdc8 100644 --- a/manim/_config/utils.py +++ b/manim/_config/utils.py @@ -27,9 +27,9 @@ from .. import constants from ..constants import RendererType +from ..typing import StrPath from ..utils.color import ManimColor from ..utils.tex import TexTemplate, TexTemplateFromFile -from ..utils.tex_templates import TexTemplateLibrary def config_file_paths() -> list[Path]: @@ -76,7 +76,7 @@ def config_file_paths() -> list[Path]: def make_config_parser( - custom_file: str | os.PathLike | None = None, + custom_file: StrPath | None = None, ) -> configparser.ConfigParser: """Make a :class:`ConfigParser` object and load any ``.cfg`` files. diff --git a/manim/animation/changing.py b/manim/animation/changing.py index 0354a1c002..bb11cfc0a4 100644 --- a/manim/animation/changing.py +++ b/manim/animation/changing.py @@ -6,7 +6,6 @@ from typing import Callable -from manim._config import config from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.types.vectorized_mobject import VGroup, VMobject from manim.utils.color import ( diff --git a/manim/animation/creation.py b/manim/animation/creation.py index f28c123025..6f8173e35a 100644 --- a/manim/animation/creation.py +++ b/manim/animation/creation.py @@ -92,7 +92,7 @@ def construct(self): from ..mobject.mobject import Group, Mobject from ..mobject.types.vectorized_mobject import VMobject from ..utils.bezier import integer_interpolate -from ..utils.rate_functions import double_smooth, linear, smooth +from ..utils.rate_functions import double_smooth, linear class ShowPartial(Animation): diff --git a/manim/animation/indication.py b/manim/animation/indication.py index fa3d801b24..e0ba47430e 100644 --- a/manim/animation/indication.py +++ b/manim/animation/indication.py @@ -45,6 +45,7 @@ def construct(self): from manim.mobject.geometry.line import Line from manim.mobject.geometry.polygram import Rectangle from manim.mobject.geometry.shape_matchers import SurroundingRectangle +from manim.scene.scene import Scene from .. import config from ..animation.animation import Animation @@ -313,7 +314,7 @@ def _get_bounds(self, alpha: float) -> Tuple[float]: lower = max(lower, 0) return (lower, upper) - def clean_up_from_scene(self, scene: "Scene") -> None: + def clean_up_from_scene(self, scene: Scene) -> None: super().clean_up_from_scene(scene) for submob, start in self.get_all_families_zipped(): submob.pointwise_become_partial(start, 0, 1) diff --git a/manim/camera/camera.py b/manim/camera/camera.py index b26e4aec0e..fe7f58ef23 100644 --- a/manim/camera/camera.py +++ b/manim/camera/camera.py @@ -9,7 +9,6 @@ import itertools as it import operator as op import pathlib -import time from functools import reduce from typing import Any, Callable, Iterable diff --git a/manim/constants.py b/manim/constants.py index 1f6a8684e6..065a10fcfc 100644 --- a/manim/constants.py +++ b/manim/constants.py @@ -10,6 +10,8 @@ from cloup import Context from PIL.Image import Resampling +from manim.typing import Vector3 + __all__ = [ "SCENE_NOT_FOUND_MESSAGE", "CHOOSE_NUMBER_MESSAGE", @@ -77,34 +79,34 @@ ] # Messages -SCENE_NOT_FOUND_MESSAGE: str = """ +SCENE_NOT_FOUND_MESSAGE = """ {} is not in the script """ -CHOOSE_NUMBER_MESSAGE: str = """ +CHOOSE_NUMBER_MESSAGE = """ Choose number corresponding to desired scene/arguments. (Use comma separated list for multiple entries) Choice(s): """ -INVALID_NUMBER_MESSAGE: str = "Invalid scene numbers have been specified. Aborting." -NO_SCENE_MESSAGE: str = """ +INVALID_NUMBER_MESSAGE = "Invalid scene numbers have been specified. Aborting." +NO_SCENE_MESSAGE = """ There are no scenes inside that module """ # Pango stuff -NORMAL: str = "NORMAL" -ITALIC: str = "ITALIC" -OBLIQUE: str = "OBLIQUE" -BOLD: str = "BOLD" +NORMAL = "NORMAL" +ITALIC = "ITALIC" +OBLIQUE = "OBLIQUE" +BOLD = "BOLD" # Only for Pango from below -THIN: str = "THIN" -ULTRALIGHT: str = "ULTRALIGHT" -LIGHT: str = "LIGHT" -SEMILIGHT: str = "SEMILIGHT" -BOOK: str = "BOOK" -MEDIUM: str = "MEDIUM" -SEMIBOLD: str = "SEMIBOLD" -ULTRABOLD: str = "ULTRABOLD" -HEAVY: str = "HEAVY" -ULTRAHEAVY: str = "ULTRAHEAVY" +THIN = "THIN" +ULTRALIGHT = "ULTRALIGHT" +LIGHT = "LIGHT" +SEMILIGHT = "SEMILIGHT" +BOOK = "BOOK" +MEDIUM = "MEDIUM" +SEMIBOLD = "SEMIBOLD" +ULTRABOLD = "ULTRABOLD" +HEAVY = "HEAVY" +ULTRAHEAVY = "ULTRAHEAVY" RESAMPLING_ALGORITHMS = { "nearest": Resampling.NEAREST, @@ -120,80 +122,80 @@ } # Geometry: directions -ORIGIN: np.ndarray = np.array((0.0, 0.0, 0.0)) +ORIGIN: Vector3 = np.array((0.0, 0.0, 0.0)) """The center of the coordinate system.""" -UP: np.ndarray = np.array((0.0, 1.0, 0.0)) +UP: Vector3 = np.array((0.0, 1.0, 0.0)) """One unit step in the positive Y direction.""" -DOWN: np.ndarray = np.array((0.0, -1.0, 0.0)) +DOWN: Vector3 = np.array((0.0, -1.0, 0.0)) """One unit step in the negative Y direction.""" -RIGHT: np.ndarray = np.array((1.0, 0.0, 0.0)) +RIGHT: Vector3 = np.array((1.0, 0.0, 0.0)) """One unit step in the positive X direction.""" -LEFT: np.ndarray = np.array((-1.0, 0.0, 0.0)) +LEFT: Vector3 = np.array((-1.0, 0.0, 0.0)) """One unit step in the negative X direction.""" -IN: np.ndarray = np.array((0.0, 0.0, -1.0)) +IN: Vector3 = np.array((0.0, 0.0, -1.0)) """One unit step in the negative Z direction.""" -OUT: np.ndarray = np.array((0.0, 0.0, 1.0)) +OUT: Vector3 = np.array((0.0, 0.0, 1.0)) """One unit step in the positive Z direction.""" # Geometry: axes -X_AXIS: np.ndarray = np.array((1.0, 0.0, 0.0)) -Y_AXIS: np.ndarray = np.array((0.0, 1.0, 0.0)) -Z_AXIS: np.ndarray = np.array((0.0, 0.0, 1.0)) +X_AXIS: Vector3 = np.array((1.0, 0.0, 0.0)) +Y_AXIS: Vector3 = np.array((0.0, 1.0, 0.0)) +Z_AXIS: Vector3 = np.array((0.0, 0.0, 1.0)) # Geometry: useful abbreviations for diagonals -UL: np.ndarray = UP + LEFT +UL: Vector3 = UP + LEFT """One step up plus one step left.""" -UR: np.ndarray = UP + RIGHT +UR: Vector3 = UP + RIGHT """One step up plus one step right.""" -DL: np.ndarray = DOWN + LEFT +DL: Vector3 = DOWN + LEFT """One step down plus one step left.""" -DR: np.ndarray = DOWN + RIGHT +DR: Vector3 = DOWN + RIGHT """One step down plus one step right.""" # Geometry -START_X: int = 30 -START_Y: int = 20 -DEFAULT_DOT_RADIUS: float = 0.08 -DEFAULT_SMALL_DOT_RADIUS: float = 0.04 -DEFAULT_DASH_LENGTH: float = 0.05 -DEFAULT_ARROW_TIP_LENGTH: float = 0.35 +START_X = 30 +START_Y = 20 +DEFAULT_DOT_RADIUS = 0.08 +DEFAULT_SMALL_DOT_RADIUS = 0.04 +DEFAULT_DASH_LENGTH = 0.05 +DEFAULT_ARROW_TIP_LENGTH = 0.35 # Default buffers (padding) -SMALL_BUFF: float = 0.1 -MED_SMALL_BUFF: float = 0.25 -MED_LARGE_BUFF: float = 0.5 -LARGE_BUFF: float = 1 -DEFAULT_MOBJECT_TO_EDGE_BUFFER: float = MED_LARGE_BUFF -DEFAULT_MOBJECT_TO_MOBJECT_BUFFER: float = MED_SMALL_BUFF +SMALL_BUFF = 0.1 +MED_SMALL_BUFF = 0.25 +MED_LARGE_BUFF = 0.5 +LARGE_BUFF = 1 +DEFAULT_MOBJECT_TO_EDGE_BUFFER = MED_LARGE_BUFF +DEFAULT_MOBJECT_TO_MOBJECT_BUFFER = MED_SMALL_BUFF # Times in seconds -DEFAULT_POINTWISE_FUNCTION_RUN_TIME: float = 3.0 -DEFAULT_WAIT_TIME: float = 1.0 +DEFAULT_POINTWISE_FUNCTION_RUN_TIME = 3.0 +DEFAULT_WAIT_TIME = 1.0 # Misc -DEFAULT_POINT_DENSITY_2D: int = 25 -DEFAULT_POINT_DENSITY_1D: int = 10 -DEFAULT_STROKE_WIDTH: int = 4 -DEFAULT_FONT_SIZE: float = 48 -SCALE_FACTOR_PER_FONT_POINT: float = 1 / 960 +DEFAULT_POINT_DENSITY_2D = 25 +DEFAULT_POINT_DENSITY_1D = 10 +DEFAULT_STROKE_WIDTH = 4 +DEFAULT_FONT_SIZE = 48 +SCALE_FACTOR_PER_FONT_POINT = 1 / 960 # Mathematical constants -PI: float = np.pi +PI = np.pi """The ratio of the circumference of a circle to its diameter.""" -TAU: float = 2 * PI +TAU = 2 * PI """The ratio of the circumference of a circle to its radius.""" -DEGREES: float = TAU / 360 +DEGREES = TAU / 360 """The exchange rate between radians and degrees.""" # Video qualities @@ -236,7 +238,7 @@ }, } -DEFAULT_QUALITY: str = "high_quality" +DEFAULT_QUALITY = "high_quality" EPILOG = "Made with <3 by Manim Community developers." SHIFT_VALUE = 65505 diff --git a/manim/mobject/geometry/arc.py b/manim/mobject/geometry/arc.py index 5672907187..9b724482c5 100644 --- a/manim/mobject/geometry/arc.py +++ b/manim/mobject/geometry/arc.py @@ -43,16 +43,16 @@ def construct(self): ] import itertools -import math import warnings -from typing import TYPE_CHECKING, Sequence +from typing import TYPE_CHECKING import numpy as np +from typing_extensions import Self from manim.constants import * from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL -from manim.mobject.types.vectorized_mobject import VMobject -from manim.utils.color import * +from manim.mobject.types.vectorized_mobject import VGroup, VMobject +from manim.utils.color import BLACK, BLUE, RED, WHITE, ParsableManimColor from manim.utils.iterables import adjacent_pairs from manim.utils.space_ops import ( angle_of_vector, @@ -63,9 +63,11 @@ def construct(self): ) if TYPE_CHECKING: + import manim.mobject.geometry.tips as tips from manim.mobject.mobject import Mobject from manim.mobject.text.tex_mobject import SingleStringMathTex, Tex from manim.mobject.text.text_mobject import Text + from manim.typing import CubicBezierPoints, Point3D, QuadraticBezierPoints, Vector class TipableVMobject(VMobject, metaclass=ConvertToOpenGL): @@ -88,21 +90,26 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL): def __init__( self, - tip_length=DEFAULT_ARROW_TIP_LENGTH, - normal_vector=OUT, - tip_style={}, + tip_length: float = DEFAULT_ARROW_TIP_LENGTH, + normal_vector: Vector = OUT, + tip_style: dict = {}, **kwargs, - ): - self.tip_length = tip_length - self.normal_vector = normal_vector - self.tip_style = tip_style + ) -> None: + self.tip_length: float = tip_length + self.normal_vector: Vector = normal_vector + self.tip_style: dict = tip_style super().__init__(**kwargs) # Adding, Creating, Modifying tips def add_tip( - self, tip=None, tip_shape=None, tip_length=None, tip_width=None, at_start=False - ): + self, + tip: tips.ArrowTip | None = None, + tip_shape: type[tips.ArrowTip] | None = None, + tip_length: float | None = None, + tip_width: float | None = None, + at_start: bool = False, + ) -> Self: """Adds a tip to the TipableVMobject instance, recognising that the endpoints might need to be switched if it's a 'starting tip' or not. @@ -117,7 +124,11 @@ def add_tip( return self def create_tip( - self, tip_shape=None, tip_length=None, tip_width=None, at_start=False + self, + tip_shape: type[tips.ArrowTip] | None = None, + tip_length: float = None, + tip_width: float = None, + at_start: bool = False, ): """Stylises the tip, positions it spatially, and returns the newly instantiated tip to the caller. @@ -126,7 +137,12 @@ def create_tip( self.position_tip(tip, at_start) return tip - def get_unpositioned_tip(self, tip_shape=None, tip_length=None, tip_width=None): + def get_unpositioned_tip( + self, + tip_shape: type[tips.ArrowTip] | None = None, + tip_length: float | None = None, + tip_width: float | None = None, + ): """Returns a tip that has been stylistically configured, but has not yet been given a position in space. """ @@ -150,7 +166,7 @@ def get_unpositioned_tip(self, tip_shape=None, tip_length=None, tip_width=None): tip = tip_shape(length=tip_length, **style) return tip - def position_tip(self, tip, at_start=False): + def position_tip(self, tip: tips.ArrowTip, at_start: bool = False): # Last two control points, defining both # the end, and the tangency direction if at_start: @@ -177,7 +193,7 @@ def position_tip(self, tip, at_start=False): tip.shift(anchor - tip.tip_point) return tip - def reset_endpoints_based_on_tip(self, tip, at_start): + def reset_endpoints_based_on_tip(self, tip: tips.ArrowTip, at_start: bool) -> Self: if self.get_length() == 0: # Zero length, put_start_and_end_on wouldn't work return self @@ -188,7 +204,7 @@ def reset_endpoints_based_on_tip(self, tip, at_start): self.put_start_and_end_on(self.get_start(), tip.base) return self - def asign_tip_attr(self, tip, at_start): + def asign_tip_attr(self, tip: tips.ArrowTip, at_start: bool) -> Self: if at_start: self.start_tip = tip else: @@ -197,15 +213,15 @@ def asign_tip_attr(self, tip, at_start): # Checking for tips - def has_tip(self): + def has_tip(self) -> bool: return hasattr(self, "tip") and self.tip in self - def has_start_tip(self): + def has_start_tip(self) -> bool: return hasattr(self, "start_tip") and self.start_tip in self # Getters - def pop_tips(self): + def pop_tips(self) -> VGroup: start, end = self.get_start_and_end() result = self.get_group_class()() if self.has_tip(): @@ -217,7 +233,7 @@ def pop_tips(self): self.put_start_and_end_on(start, end) return result - def get_tips(self): + def get_tips(self) -> VGroup: """Returns a VGroup (collection of VMobjects) containing the TipableVMObject instance's tips. """ @@ -237,28 +253,28 @@ def get_tip(self): else: return tips[0] - def get_default_tip_length(self): + def get_default_tip_length(self) -> float: return self.tip_length - def get_first_handle(self): + def get_first_handle(self) -> Point3D: return self.points[1] - def get_last_handle(self): + def get_last_handle(self) -> Point3D: return self.points[-2] - def get_end(self): + def get_end(self) -> Point3D: if self.has_tip(): return self.tip.get_start() else: return super().get_end() - def get_start(self): + def get_start(self) -> Point3D: if self.has_start_tip(): return self.start_tip.get_start() else: return super().get_start() - def get_length(self): + def get_length(self) -> np.floating: start, end = self.get_start_and_end() return np.linalg.norm(start - end) @@ -283,21 +299,21 @@ def __init__( radius: float = 1.0, start_angle: float = 0, angle: float = TAU / 4, - num_components=9, - arc_center=ORIGIN, + num_components: int = 9, + arc_center: Point3D = ORIGIN, **kwargs, ): if radius is None: # apparently None is passed by ArcBetweenPoints radius = 1.0 self.radius = radius - self.num_components = num_components - self.arc_center = arc_center - self.start_angle = start_angle - self.angle = angle - self._failed_to_get_center = False + self.num_components: int = num_components + self.arc_center: Point3D = arc_center + self.start_angle: float = start_angle + self.angle: float = angle + self._failed_to_get_center: bool = False super().__init__(**kwargs) - def generate_points(self): + def generate_points(self) -> None: self._set_pre_positioned_points() self.scale(self.radius, about_point=ORIGIN) self.shift(self.arc_center) @@ -305,7 +321,7 @@ def generate_points(self): # Points are set a bit differently when rendering via OpenGL. # TODO: refactor Arc so that only one strategy for setting points # has to be used. - def init_points(self): + def init_points(self) -> None: self.set_points( Arc._create_quadratic_bezier_points( angle=self.angle, @@ -317,7 +333,9 @@ def init_points(self): self.shift(self.arc_center) @staticmethod - def _create_quadratic_bezier_points(angle, start_angle=0, n_components=8): + def _create_quadratic_bezier_points( + angle: float, start_angle: float = 0, n_components: int = 8 + ) -> QuadraticBezierPoints: samples = np.array( [ [np.cos(a), np.sin(a), 0] @@ -337,7 +355,7 @@ def _create_quadratic_bezier_points(angle, start_angle=0, n_components=8): points[2::3] = samples[2::2] return points - def _set_pre_positioned_points(self): + def _set_pre_positioned_points(self) -> None: anchors = np.array( [ np.cos(a) * RIGHT + np.sin(a) * UP @@ -360,7 +378,7 @@ def _set_pre_positioned_points(self): handles2 = anchors[1:] - (d_theta / 3) * tangent_vectors[1:] self.set_anchors_and_handles(anchors[:-1], handles1, handles2, anchors[1:]) - def get_arc_center(self, warning=True): + def get_arc_center(self, warning: bool = True) -> Point3D: """Looks at the normals to the first two anchors, and finds their intersection points """ @@ -386,11 +404,11 @@ def get_arc_center(self, warning=True): self._failed_to_get_center = True return np.array(ORIGIN) - def move_arc_center_to(self, point): + def move_arc_center_to(self, point: Point3D) -> Self: self.shift(point - self.get_arc_center()) return self - def stop_angle(self): + def stop_angle(self) -> float: return angle_of_vector(self.points[-1] - self.get_arc_center()) % TAU @@ -413,7 +431,14 @@ def construct(self): self.play(Create(arc)) """ - def __init__(self, start, end, angle=TAU / 4, radius=None, **kwargs): + def __init__( + self, + start: Point3D, + end: Point3D, + angle: float = TAU / 4, + radius: float = None, + **kwargs, + ) -> None: if radius is not None: self.radius = radius if radius < 0: @@ -427,8 +452,8 @@ def __init__(self, start, end, angle=TAU / 4, radius=None, **kwargs): """ArcBetweenPoints called with a radius that is smaller than half the distance between the points.""", ) - arc_height = radius - math.sqrt(radius**2 - halfdist**2) - angle = math.acos((radius - arc_height) / radius) * sign + arc_height = radius - np.sqrt(radius**2 - halfdist**2) + angle = np.arccos((radius - arc_height) / radius) * sign super().__init__(radius=radius, angle=angle, **kwargs) if angle == 0: @@ -440,11 +465,11 @@ def __init__(self, start, end, angle=TAU / 4, radius=None, **kwargs): if not self._failed_to_get_center: self.radius = np.linalg.norm(np.array(start) - np.array(center)) else: - self.radius = math.inf + self.radius = np.inf class CurvedArrow(ArcBetweenPoints): - def __init__(self, start_point, end_point, **kwargs): + def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs) -> None: from manim.mobject.geometry.tips import ArrowTriangleFilledTip tip_shape = kwargs.pop("tip_shape", ArrowTriangleFilledTip) @@ -453,7 +478,7 @@ def __init__(self, start_point, end_point, **kwargs): class CurvedDoubleArrow(CurvedArrow): - def __init__(self, start_point, end_point, **kwargs): + def __init__(self, start_point: Point3D, end_point: Point3D, **kwargs) -> None: if "tip_shape_end" in kwargs: kwargs["tip_shape"] = kwargs.pop("tip_shape_end") from manim.mobject.geometry.tips import ArrowTriangleFilledTip @@ -493,7 +518,7 @@ def __init__( radius: float | None = None, color: ParsableManimColor = RED, **kwargs, - ): + ) -> None: super().__init__( radius=radius, start_angle=0, @@ -508,7 +533,7 @@ def surround( dim_to_match: int = 0, stretch: bool = False, buffer_factor: float = 1.2, - ): + ) -> Self: """Modifies a circle so that it surrounds a given mobject. Parameters @@ -555,7 +580,7 @@ def construct(self): self.width = np.sqrt(mobject.width**2 + mobject.height**2) return self.scale(buffer_factor) - def point_at_angle(self, angle: float): + def point_at_angle(self, angle: float) -> Point3D: """Returns the position of a point on the circle. Parameters @@ -587,13 +612,11 @@ def construct(self): start_angle = angle_of_vector(self.points[0] - self.get_center()) proportion = (angle - start_angle) / TAU - proportion -= math.floor(proportion) + proportion -= np.floor(proportion) return self.point_from_proportion(proportion) @staticmethod - def from_three_points( - p1: Sequence[float], p2: Sequence[float], p3: Sequence[float], **kwargs - ): + def from_three_points(p1: Point3D, p2: Point3D, p3: Point3D, **kwargs) -> Self: """Returns a circle passing through the specified three points. @@ -653,13 +676,13 @@ def construct(self): def __init__( self, - point: list | np.ndarray = ORIGIN, + point: Point3D = ORIGIN, radius: float = DEFAULT_DOT_RADIUS, stroke_width: float = 0, fill_opacity: float = 1.0, color: ParsableManimColor = WHITE, **kwargs, - ): + ) -> None: super().__init__( arc_center=point, radius=radius, @@ -676,11 +699,11 @@ class AnnotationDot(Dot): def __init__( self, radius: float = DEFAULT_DOT_RADIUS * 1.3, - stroke_width=5, - stroke_color=WHITE, - fill_color=BLUE, + stroke_width: float = 5, + stroke_color: ParsableManimColor = WHITE, + fill_color: ParsableManimColor = BLUE, **kwargs, - ): + ) -> None: super().__init__( radius=radius, stroke_width=stroke_width, @@ -769,7 +792,7 @@ def construct(self): self.add(ellipse_group) """ - def __init__(self, width: float = 2, height: float = 1, **kwargs): + def __init__(self, width: float = 2, height: float = 1, **kwargs) -> None: super().__init__(**kwargs) self.stretch_to_fit_width(width) self.stretch_to_fit_height(height) @@ -823,15 +846,15 @@ def construct(self): def __init__( self, - inner_radius=1, - outer_radius=2, - angle=TAU / 4, - start_angle=0, - fill_opacity=1, - stroke_width=0, - color=WHITE, + inner_radius: float = 1, + outer_radius: float = 2, + angle: float = TAU / 4, + start_angle: float = 0, + fill_opacity: float = 1, + stroke_width: float = 0, + color: ParsableManimColor = WHITE, **kwargs, - ): + ) -> None: self.inner_radius = inner_radius self.outer_radius = outer_radius super().__init__( @@ -843,7 +866,7 @@ def __init__( **kwargs, ) - def generate_points(self): + def generate_points(self) -> None: inner_arc, outer_arc = ( Arc( start_angle=self.start_angle, @@ -879,7 +902,9 @@ def construct(self): self.add(sector, sector2) """ - def __init__(self, outer_radius=1, inner_radius=0, **kwargs): + def __init__( + self, outer_radius: float = 1, inner_radius: float = 0, **kwargs + ) -> None: super().__init__(inner_radius=inner_radius, outer_radius=outer_radius, **kwargs) @@ -911,12 +936,12 @@ def __init__( self, inner_radius: float | None = 1, outer_radius: float | None = 2, - fill_opacity=1, - stroke_width=0, - color=WHITE, - mark_paths_closed=False, + fill_opacity: float = 1, + stroke_width: float = 0, + color: ParsableManimColor = WHITE, + mark_paths_closed: bool = False, **kwargs, - ): + ) -> None: self.mark_paths_closed = mark_paths_closed # is this even used? self.inner_radius = inner_radius self.outer_radius = outer_radius @@ -924,7 +949,7 @@ def __init__( fill_opacity=fill_opacity, stroke_width=stroke_width, color=color, **kwargs ) - def generate_points(self): + def generate_points(self) -> None: self.radius = self.outer_radius outer_circle = Circle(radius=self.outer_radius) inner_circle = Circle(radius=self.inner_radius) @@ -959,7 +984,14 @@ def construct(self): """ - def __init__(self, start_anchor, start_handle, end_handle, end_anchor, **kwargs): + def __init__( + self, + start_anchor: CubicBezierPoints, + start_handle: CubicBezierPoints, + end_handle: CubicBezierPoints, + end_anchor: CubicBezierPoints, + **kwargs, + ) -> None: super().__init__(**kwargs) self.add_cubic_bezier_curve(start_anchor, start_handle, end_handle, end_anchor) @@ -1045,12 +1077,12 @@ def construct(self): def __init__( self, - *vertices: list | np.ndarray, + *vertices: Point3D, angle: float = PI / 4, radius: float | None = None, arc_config: list[dict] | None = None, **kwargs, - ): + ) -> None: n = len(vertices) point_pairs = [(vertices[k], vertices[(k + 1) % n]) for k in range(n)] @@ -1188,7 +1220,7 @@ def construct(self): self.wait(2) """ - def __init__(self, *arcs: Arc | ArcBetweenPoints, **kwargs): + def __init__(self, *arcs: Arc | ArcBetweenPoints, **kwargs) -> None: if not all(isinstance(m, (Arc, ArcBetweenPoints)) for m in arcs): raise ValueError( "All ArcPolygon submobjects must be of type Arc/ArcBetweenPoints", @@ -1207,7 +1239,7 @@ def __init__(self, *arcs: Arc | ArcBetweenPoints, **kwargs): self.append_points(arc1.points) line = Line(arc1.get_end(), arc2.get_start()) len_ratio = line.get_length() / arc1.get_arc_length() - if math.isnan(len_ratio) or math.isinf(len_ratio): + if np.isnan(len_ratio) or np.isinf(len_ratio): continue line.insert_n_curves(int(arc1.get_num_curves() * len_ratio)) self.append_points(line.points) diff --git a/manim/mobject/geometry/boolean_ops.py b/manim/mobject/geometry/boolean_ops.py index 9d3ccf034c..0bc33d70b0 100644 --- a/manim/mobject/geometry/boolean_ops.py +++ b/manim/mobject/geometry/boolean_ops.py @@ -2,8 +2,6 @@ from __future__ import annotations -import typing - import numpy as np from pathops import Path as SkiaPath from pathops import PathVerb, difference, intersection, union, xor @@ -11,6 +9,7 @@ from manim import config from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.types.vectorized_mobject import VMobject +from manim.typing import Point2D_Array from ...constants import RendererType @@ -23,12 +22,9 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL): objects (:class:`~.VMobject`). """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - def _convert_2d_to_3d_array( self, - points: typing.Iterable, + points: Point2D_Array, z_dim: float = 0.0, ) -> list[np.ndarray]: """Converts an iterable with coordinates in 2d to 3d by adding @@ -43,7 +39,7 @@ def _convert_2d_to_3d_array( Returns ------- - typing.List[np.ndarray] + Point2D_Array A list of array converted to 3d. Example @@ -216,7 +212,7 @@ def construct(self): """ - def __init__(self, subject, clip, **kwargs) -> None: + def __init__(self, subject: VMobject, clip: VMobject, **kwargs) -> None: super().__init__(**kwargs) outpen = SkiaPath() difference( @@ -258,7 +254,7 @@ def construct(self): """ - def __init__(self, *vmobjects, **kwargs) -> None: + def __init__(self, *vmobjects: VMobject, **kwargs) -> None: if len(vmobjects) < 2: raise ValueError("At least 2 mobjects needed for Intersection.") @@ -311,7 +307,7 @@ def construct(self): """ - def __init__(self, subject, clip, **kwargs) -> None: + def __init__(self, subject: VMobject, clip: VMobject, **kwargs) -> None: super().__init__(**kwargs) outpen = SkiaPath() xor( diff --git a/manim/mobject/geometry/line.py b/manim/mobject/geometry/line.py index ce556081f4..b958e55e19 100644 --- a/manim/mobject/geometry/line.py +++ b/manim/mobject/geometry/line.py @@ -14,9 +14,10 @@ "RightAngle", ] -from typing import Any, Sequence +from typing import TYPE_CHECKING import numpy as np +from typing_extensions import Self from manim import config from manim.constants import * @@ -26,19 +27,32 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.opengl.opengl_mobject import OpenGLMobject from manim.mobject.types.vectorized_mobject import DashedVMobject, VGroup, VMobject -from manim.utils.color import * +from manim.utils.color import WHITE from manim.utils.space_ops import angle_of_vector, line_intersection, normalize +if TYPE_CHECKING: + from manim.typing import Point2D, Point3D, Vector + from manim.utils.color import ParsableManimColor + + from ..matrix import Matrix # Avoid circular import + class Line(TipableVMobject): - def __init__(self, start=LEFT, end=RIGHT, buff=0, path_arc=None, **kwargs): + def __init__( + self, + start: Point3D = LEFT, + end: Point3D = RIGHT, + buff: float = 0, + path_arc: float | None = None, + **kwargs, + ) -> None: self.dim = 3 self.buff = buff self.path_arc = path_arc self._set_start_and_end_attrs(start, end) super().__init__(**kwargs) - def generate_points(self): + def generate_points(self) -> None: self.set_points_by_ends( start=self.start, end=self.end, @@ -46,7 +60,13 @@ def generate_points(self): path_arc=self.path_arc, ) - def set_points_by_ends(self, start, end, buff=0, path_arc=0): + def set_points_by_ends( + self, + start: Point3D, + end: Point3D, + buff: float = 0, + path_arc: float = 0, + ) -> None: if path_arc: arc = ArcBetweenPoints(self.start, self.end, angle=self.path_arc) self.set_points(arc.points) @@ -57,7 +77,7 @@ def set_points_by_ends(self, start, end, buff=0, path_arc=0): init_points = generate_points - def _account_for_buff(self, buff): + def _account_for_buff(self, buff: float) -> Self: if buff == 0: return # @@ -72,7 +92,7 @@ def _account_for_buff(self, buff): self.pointwise_become_partial(self, buff_proportion, 1 - buff_proportion) return self - def _set_start_and_end_attrs(self, start, end): + def _set_start_and_end_attrs(self, start: Point3D, end: Point3D) -> None: # If either start or end are Mobjects, this # gives their centers rough_start = self._pointify(start) @@ -86,9 +106,9 @@ def _set_start_and_end_attrs(self, start, end): def _pointify( self, - mob_or_point: Mobject | Sequence[float], - direction: Sequence[float] | None = None, - ) -> np.ndarray: + mob_or_point: Mobject | Point3D, + direction: Vector | None = None, + ) -> Point3D: """Transforms a mobject into its corresponding point. Does nothing if a point is passed. ``direction`` determines the location of the point along its bounding box in that direction. @@ -108,11 +128,11 @@ def _pointify( return mob.get_boundary_point(direction) return np.array(mob_or_point) - def set_path_arc(self, new_value): + def set_path_arc(self, new_value: float) -> None: self.path_arc = new_value self.init_points() - def put_start_and_end_on(self, start: Sequence[float], end: Sequence[float]): + def put_start_and_end_on(self, start: Point3D, end: Point3D) -> Self: """Sets starts and end coordinates of a line. Examples @@ -143,16 +163,16 @@ def construct(self): self.generate_points() return super().put_start_and_end_on(start, end) - def get_vector(self): + def get_vector(self) -> Vector: return self.get_end() - self.get_start() - def get_unit_vector(self): + def get_unit_vector(self) -> Vector: return normalize(self.get_vector()) - def get_angle(self): + def get_angle(self) -> float: return angle_of_vector(self.get_vector()) - def get_projection(self, point: Sequence[float]) -> Sequence[float]: + def get_projection(self, point: Point3D) -> Vector: """Returns the projection of a point onto a line. Parameters @@ -166,10 +186,10 @@ def get_projection(self, point: Sequence[float]) -> Sequence[float]: unit_vect = normalize(end - start) return start + np.dot(point - start, unit_vect) * unit_vect - def get_slope(self): + def get_slope(self) -> float: return np.tan(self.get_angle()) - def set_angle(self, angle, about_point=None): + def set_angle(self, angle: float, about_point: Point3D | None = None) -> Self: if about_point is None: about_point = self.get_start() @@ -180,7 +200,7 @@ def set_angle(self, angle, about_point=None): return self - def set_length(self, length): + def set_length(self, length: float) -> Self: return self.scale(length / self.get_length()) @@ -220,11 +240,11 @@ def construct(self): def __init__( self, - *args: Any, + *args, dash_length: float = DEFAULT_DASH_LENGTH, dashed_ratio: float = 0.5, **kwargs, - ): + ) -> None: self.dash_length = dash_length self.dashed_ratio = dashed_ratio super().__init__(*args, **kwargs) @@ -253,7 +273,7 @@ def _calculate_num_dashes(self) -> int: int(np.ceil((self.get_length() / self.dash_length) * self.dashed_ratio)), ) - def get_start(self) -> np.ndarray: + def get_start(self) -> Point3D: """Returns the start point of the line. Examples @@ -269,7 +289,7 @@ def get_start(self) -> np.ndarray: else: return super().get_start() - def get_end(self) -> np.ndarray: + def get_end(self) -> Point3D: """Returns the end point of the line. Examples @@ -285,7 +305,7 @@ def get_end(self) -> np.ndarray: else: return super().get_end() - def get_first_handle(self) -> np.ndarray: + def get_first_handle(self) -> Point3D: """Returns the point of the first handle. Examples @@ -298,7 +318,7 @@ def get_first_handle(self) -> np.ndarray: return self.submobjects[0].points[1] - def get_last_handle(self) -> np.ndarray: + def get_last_handle(self) -> Point3D: """Returns the point of the last handle. Examples @@ -352,7 +372,7 @@ def __init__( length: float = 1, d_alpha: float = 1e-6, **kwargs, - ): + ) -> None: self.length = length self.d_alpha = d_alpha da = self.d_alpha @@ -394,7 +414,7 @@ def construct(self): self.add(elbow_group) """ - def __init__(self, width: float = 0.2, angle: float = 0, **kwargs): + def __init__(self, width: float = 0.2, angle: float = 0, **kwargs) -> None: self.angle = angle super().__init__(**kwargs) self.set_points_as_corners([UP, UP + RIGHT, RIGHT]) @@ -492,13 +512,13 @@ def construct(self): def __init__( self, - *args: Any, + *args, stroke_width: float = 6, buff: float = MED_SMALL_BUFF, max_tip_length_to_length_ratio: float = 0.25, max_stroke_width_to_length_ratio: float = 5, **kwargs, - ): + ) -> None: self.max_tip_length_to_length_ratio = max_tip_length_to_length_ratio self.max_stroke_width_to_length_ratio = max_stroke_width_to_length_ratio tip_shape = kwargs.pop("tip_shape", ArrowTriangleFilledTip) @@ -509,7 +529,7 @@ def __init__( self.add_tip(tip_shape=tip_shape) self._set_stroke_width_from_length() - def scale(self, factor, scale_tips=False, **kwargs): + def scale(self, factor: float, scale_tips: bool = False, **kwargs) -> Self: r"""Scale an arrow, but keep stroke width and arrow tip size fixed. @@ -559,7 +579,7 @@ def scale(self, factor, scale_tips=False, **kwargs): self.add_tip(tip=old_tips[1], at_start=True) return self - def get_normal_vector(self) -> np.ndarray: + def get_normal_vector(self) -> Vector: """Returns the normal of a vector. Examples @@ -573,7 +593,7 @@ def get_normal_vector(self) -> np.ndarray: p0, p1, p2 = self.tip.get_start_anchors()[:3] return normalize(np.cross(p2 - p1, p1 - p0)) - def reset_normal_vector(self): + def reset_normal_vector(self) -> Self: """Resets the normal of a vector""" self.normal_vector = self.get_normal_vector() return self @@ -593,7 +613,7 @@ def get_default_tip_length(self) -> float: max_ratio = self.max_tip_length_to_length_ratio return min(self.tip_length, max_ratio * self.get_length()) - def _set_stroke_width_from_length(self): + def _set_stroke_width_from_length(self) -> Self: """Sets stroke width based on length.""" max_ratio = self.max_stroke_width_to_length_ratio if config.renderer == RendererType.OPENGL: @@ -634,7 +654,7 @@ def construct(self): self.add(plane, vector_1, vector_2) """ - def __init__(self, direction: list | np.ndarray = RIGHT, buff: float = 0, **kwargs): + def __init__(self, direction: Vector = RIGHT, buff: float = 0, **kwargs) -> None: self.buff = buff if len(direction) == 2: direction = np.hstack([direction, 0]) @@ -647,7 +667,7 @@ def coordinate_label( n_dim: int = 2, color: ParsableManimColor | None = None, **kwargs, - ): + ) -> Matrix: """Creates a label based on the coordinates of the vector. Parameters @@ -750,7 +770,7 @@ def construct(self): self.add(box, d1, d2, d3) """ - def __init__(self, *args: Any, **kwargs): + def __init__(self, *args, **kwargs) -> None: if "tip_shape_end" in kwargs: kwargs["tip_shape"] = kwargs.pop("tip_shape_end") tip_shape_start = kwargs.pop("tip_shape_start", ArrowTriangleFilledTip) @@ -871,8 +891,8 @@ def __init__( self, line1: Line, line2: Line, - radius: float = None, - quadrant: Sequence[int] = (1, 1), + radius: float | None = None, + quadrant: Point2D = (1, 1), other_angle: bool = False, dot: bool = False, dot_radius: float | None = None, @@ -880,7 +900,7 @@ def __init__( dot_color: ParsableManimColor = WHITE, elbow: bool = False, **kwargs, - ): + ) -> None: super().__init__(**kwargs) self.lines = (line1, line2) self.quadrant = quadrant @@ -1017,14 +1037,10 @@ def construct(self): self.add(line1, line2, angle, value) """ - if degrees: - return self.angle_value / DEGREES - return self.angle_value + return self.angle_value / DEGREES if degrees else self.angle_value @staticmethod - def from_three_points( - A: np.ndarray, B: np.ndarray, C: np.ndarray, **kwargs - ) -> Angle: + def from_three_points(A: Point3D, B: Point3D, C: Point3D, **kwargs) -> Angle: """The angle between the lines AB and BC. This constructs the angle :math:`\\angle ABC`. @@ -1099,5 +1115,7 @@ def construct(self): self.add(plots) """ - def __init__(self, line1: Line, line2: Line, length: float | None = None, **kwargs): + def __init__( + self, line1: Line, line2: Line, length: float | None = None, **kwargs + ) -> None: super().__init__(line1, line2, radius=length, elbow=True, **kwargs) diff --git a/manim/mobject/geometry/polygram.py b/manim/mobject/geometry/polygram.py index 5064ff737e..bf2a92bdb5 100644 --- a/manim/mobject/geometry/polygram.py +++ b/manim/mobject/geometry/polygram.py @@ -15,8 +15,9 @@ "Cutout", ] + from math import ceil -from typing import Iterable, Sequence +from typing import TYPE_CHECKING import numpy as np @@ -24,10 +25,16 @@ from manim.mobject.geometry.arc import ArcBetweenPoints from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.types.vectorized_mobject import VGroup, VMobject -from manim.utils.color import * +from manim.utils.color import BLUE, WHITE, ParsableManimColor from manim.utils.iterables import adjacent_n_tuples, adjacent_pairs from manim.utils.space_ops import angle_between_vectors, normalize, regular_vertices +if TYPE_CHECKING: + from typing_extensions import Self + + from manim.typing import Point3D, Point3D_Array + from manim.utils.color import ParsableManimColor + class Polygram(VMobject, metaclass=ConvertToOpenGL): """A generalized :class:`Polygon`, allowing for disconnected sets of edges. @@ -64,7 +71,9 @@ def construct(self): self.wait() """ - def __init__(self, *vertex_groups: Iterable[Sequence[float]], color=BLUE, **kwargs): + def __init__( + self, *vertex_groups: Point3D, color: ParsableManimColor = BLUE, **kwargs + ): super().__init__(color=color, **kwargs) for vertices in vertex_groups: @@ -76,7 +85,7 @@ def __init__(self, *vertex_groups: Iterable[Sequence[float]], color=BLUE, **kwar [*(np.array(vertex) for vertex in vertices), first_vertex], ) - def get_vertices(self) -> np.ndarray: + def get_vertices(self) -> Point3D_Array: """Gets the vertices of the :class:`Polygram`. Returns @@ -98,7 +107,7 @@ def get_vertices(self) -> np.ndarray: return self.get_start_anchors() - def get_vertex_groups(self) -> np.ndarray: + def get_vertex_groups(self) -> np.ndarray[Point3D_Array]: """Gets the vertex groups of the :class:`Polygram`. Returns @@ -138,7 +147,7 @@ def round_corners( radius: float | list[float] = 0.5, evenly_distribute_anchors: bool = False, components_per_rounded_corner: int = 2, - ): + ) -> Self: """Rounds off the corners of the :class:`Polygram`. Parameters @@ -303,7 +312,7 @@ def construct(self): self.add(isosceles, square_and_triangles) """ - def __init__(self, *vertices: Sequence[float], **kwargs): + def __init__(self, *vertices: Point3D, **kwargs) -> None: super().__init__(vertices, **kwargs) @@ -347,7 +356,7 @@ def __init__( radius: float = 1, start_angle: float | None = None, **kwargs, - ): + ) -> None: # Regular polygrams can be expressed by the number of their vertices # and their density. This relation can be expressed as its Schläfli # symbol: {num_vertices/density}. @@ -423,7 +432,7 @@ def construct(self): self.add(poly_group) """ - def __init__(self, n: int = 6, **kwargs): + def __init__(self, n: int = 6, **kwargs) -> None: super().__init__(n, density=1, **kwargs) @@ -495,7 +504,7 @@ def __init__( density: int = 2, start_angle: float | None = TAU / 4, **kwargs, - ): + ) -> None: inner_angle = TAU / (2 * n) if inner_radius is None: @@ -554,7 +563,7 @@ def construct(self): self.add(tri_group) """ - def __init__(self, **kwargs): + def __init__(self, **kwargs) -> None: super().__init__(n=3, **kwargs) @@ -664,7 +673,7 @@ def construct(self): self.add(square_1, square_2, square_3) """ - def __init__(self, side_length: float = 2.0, **kwargs): + def __init__(self, side_length: float = 2.0, **kwargs) -> None: self.side_length = side_length super().__init__(height=side_length, width=side_length, **kwargs) @@ -734,7 +743,7 @@ def construct(self): self.wait() """ - def __init__(self, main_shape: VMobject, *mobjects: VMobject, **kwargs): + def __init__(self, main_shape: VMobject, *mobjects: VMobject, **kwargs) -> None: super().__init__(**kwargs) self.append_points(main_shape.points) if main_shape.get_direction() == "CW": diff --git a/manim/mobject/geometry/shape_matchers.py b/manim/mobject/geometry/shape_matchers.py index 9462b3c113..296d9b9d9f 100644 --- a/manim/mobject/geometry/shape_matchers.py +++ b/manim/mobject/geometry/shape_matchers.py @@ -4,13 +4,17 @@ __all__ = ["SurroundingRectangle", "BackgroundRectangle", "Cross", "Underline"] +from typing import Any + +from typing_extensions import Self + from manim import config, logger from manim.constants import * from manim.mobject.geometry.line import Line from manim.mobject.geometry.polygram import RoundedRectangle from manim.mobject.mobject import Mobject from manim.mobject.types.vectorized_mobject import VGroup -from manim.utils.color import BLACK, RED, YELLOW, ParsableManimColor +from manim.utils.color import BLACK, RED, YELLOW, ManimColor, ParsableManimColor class SurroundingRectangle(RoundedRectangle): @@ -38,8 +42,13 @@ def construct(self): """ def __init__( - self, mobject, color=YELLOW, buff=SMALL_BUFF, corner_radius=0.0, **kwargs - ): + self, + mobject: Mobject, + color: ParsableManimColor = YELLOW, + buff: float = SMALL_BUFF, + corner_radius: float = 0.0, + **kwargs, + ) -> None: super().__init__( color=color, width=mobject.width + 2 * buff, @@ -78,7 +87,7 @@ def construct(self): def __init__( self, - mobject, + mobject: Mobject, color: ParsableManimColor | None = None, stroke_width: float = 0, stroke_opacity: float = 0, @@ -98,13 +107,13 @@ def __init__( buff=buff, **kwargs, ) - self.original_fill_opacity = self.fill_opacity + self.original_fill_opacity: float = self.fill_opacity - def pointwise_become_partial(self, mobject, a, b): + def pointwise_become_partial(self, mobject: Mobject, a: Any, b: float) -> Self: self.set_fill(opacity=b * self.original_fill_opacity) return self - def set_style(self, fill_opacity, **kwargs): + def set_style(self, fill_opacity: float, **kwargs) -> Self: # Unchangeable style, except for fill_opacity # All other style arguments are ignored super().set_style( @@ -120,7 +129,7 @@ def set_style(self, fill_opacity, **kwargs): ) return self - def get_fill_color(self): + def get_fill_color(self) -> ManimColor: return self.color @@ -153,10 +162,10 @@ def __init__( self, mobject: Mobject | None = None, stroke_color: ParsableManimColor = RED, - stroke_width: float = 6, - scale_factor: float = 1, + stroke_width: float = 6.0, + scale_factor: float = 1.0, **kwargs, - ): + ) -> None: super().__init__( Line(UP + LEFT, DOWN + RIGHT), Line(UP + RIGHT, DOWN + LEFT), **kwargs ) @@ -181,7 +190,7 @@ def construct(self): self.add(man, ul) """ - def __init__(self, mobject, buff=SMALL_BUFF, **kwargs): + def __init__(self, mobject: Mobject, buff: float = SMALL_BUFF, **kwargs) -> None: super().__init__(LEFT, RIGHT, buff=buff, **kwargs) self.match_width(mobject) self.next_to(mobject, DOWN, buff=self.buff) diff --git a/manim/mobject/geometry/tips.py b/manim/mobject/geometry/tips.py index e002c89b49..385093b765 100644 --- a/manim/mobject/geometry/tips.py +++ b/manim/mobject/geometry/tips.py @@ -13,6 +13,8 @@ "StealthTip", ] +from typing import TYPE_CHECKING + import numpy as np from manim.constants import * @@ -22,6 +24,9 @@ from manim.mobject.types.vectorized_mobject import VMobject from manim.utils.space_ops import angle_of_vector +if TYPE_CHECKING: + from manim.typing import Point3D, Vector + class ArrowTip(VMobject, metaclass=ConvertToOpenGL): r"""Base class for arrow tips. @@ -106,11 +111,11 @@ def construct(self): self.add(*big_arrows, *small_arrows, *labels) """ - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: raise NotImplementedError("Has to be implemented in inheriting subclasses.") @property - def base(self): + def base(self) -> Point3D: r"""The base point of the arrow tip. This is the point connecting to the arrow line. @@ -128,7 +133,7 @@ def base(self): return self.point_from_proportion(0.5) @property - def tip_point(self): + def tip_point(self) -> Point3D: r"""The tip point of the arrow tip. Examples @@ -144,7 +149,7 @@ def tip_point(self): return self.points[0] @property - def vector(self): + def vector(self) -> Vector: r"""The vector pointing from the base point to the tip point. Examples @@ -160,7 +165,7 @@ def vector(self): return self.tip_point - self.base @property - def tip_angle(self): + def tip_angle(self) -> float: r"""The angle of the arrow tip. Examples @@ -176,7 +181,7 @@ def tip_angle(self): return angle_of_vector(self.vector) @property - def length(self): + def length(self) -> np.floating: r"""The length of the arrow tip. Examples @@ -238,13 +243,13 @@ class ArrowTriangleTip(ArrowTip, Triangle): def __init__( self, - fill_opacity=0, - stroke_width=3, - length=DEFAULT_ARROW_TIP_LENGTH, - width=DEFAULT_ARROW_TIP_LENGTH, - start_angle=PI, + fill_opacity: float = 0, + stroke_width: float = 3, + length: float = DEFAULT_ARROW_TIP_LENGTH, + width: float = DEFAULT_ARROW_TIP_LENGTH, + start_angle: float = PI, **kwargs, - ): + ) -> None: Triangle.__init__( self, fill_opacity=fill_opacity, @@ -264,7 +269,9 @@ class ArrowTriangleFilledTip(ArrowTriangleTip): This is the default arrow tip shape. """ - def __init__(self, fill_opacity=1, stroke_width=0, **kwargs): + def __init__( + self, fill_opacity: float = 1, stroke_width: float = 0, **kwargs + ) -> None: super().__init__(fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs) @@ -273,12 +280,12 @@ class ArrowCircleTip(ArrowTip, Circle): def __init__( self, - fill_opacity=0, - stroke_width=3, - length=DEFAULT_ARROW_TIP_LENGTH, - start_angle=PI, + fill_opacity: float = 0, + stroke_width: float = 3, + length: float = DEFAULT_ARROW_TIP_LENGTH, + start_angle: float = PI, **kwargs, - ): + ) -> None: self.start_angle = start_angle Circle.__init__( self, fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs @@ -290,7 +297,9 @@ def __init__( class ArrowCircleFilledTip(ArrowCircleTip): r"""Circular arrow tip with filled tip.""" - def __init__(self, fill_opacity=1, stroke_width=0, **kwargs): + def __init__( + self, fill_opacity: float = 1, stroke_width: float = 0, **kwargs + ) -> None: super().__init__(fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs) @@ -299,12 +308,12 @@ class ArrowSquareTip(ArrowTip, Square): def __init__( self, - fill_opacity=0, - stroke_width=3, - length=DEFAULT_ARROW_TIP_LENGTH, - start_angle=PI, + fill_opacity: float = 0, + stroke_width: float = 3, + length: float = DEFAULT_ARROW_TIP_LENGTH, + start_angle: float = PI, **kwargs, - ): + ) -> None: self.start_angle = start_angle Square.__init__( self, @@ -320,5 +329,7 @@ def __init__( class ArrowSquareFilledTip(ArrowSquareTip): r"""Square arrow tip with filled tip.""" - def __init__(self, fill_opacity=1, stroke_width=0, **kwargs): + def __init__( + self, fill_opacity: float = 1, stroke_width: float = 0, **kwargs + ) -> None: super().__init__(fill_opacity=fill_opacity, stroke_width=stroke_width, **kwargs) diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index a4da38dcac..c2f5744ca8 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -14,9 +14,10 @@ import fractions as fr import numbers -from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence +from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence, TypeVar, overload import numpy as np +from typing_extensions import Self from manim import config from manim.constants import * @@ -54,6 +55,9 @@ if TYPE_CHECKING: from manim.mobject.mobject import Mobject + from manim.typing import ManimFloat, Point2D, Point3D, Vector3 + + LineType = TypeVar("LineType", bound=Line) class CoordinateSystem: @@ -108,12 +112,12 @@ def construct(self): def __init__( self, - x_range=None, - y_range=None, - x_length=None, - y_length=None, - dimension=2, - ): + x_range: Sequence[float] | None = None, + y_range: Sequence[float] | None = None, + x_length: float | None = None, + y_length: float | None = None, + dimension: int = 2, + ) -> None: self.dimension = dimension default_step = 1 @@ -141,13 +145,13 @@ def __init__( self.y_length = y_length self.num_sampled_graph_points_per_tick = 10 - def coords_to_point(self, *coords): + def coords_to_point(self, *coords: Sequence[ManimFloat]): raise NotImplementedError() - def point_to_coords(self, point): + def point_to_coords(self, point: Point3D): raise NotImplementedError() - def polar_to_point(self, radius: float, azimuth: float) -> np.ndarray: + def polar_to_point(self, radius: float, azimuth: float) -> Point2D: r"""Gets a point from polar coordinates. Parameters @@ -178,7 +182,7 @@ def construct(self): """ return self.coords_to_point(radius * np.cos(azimuth), radius * np.sin(azimuth)) - def point_to_polar(self, point: np.ndarray) -> tuple[float, float]: + def point_to_polar(self, point: np.ndarray) -> Point2D: r"""Gets polar coordinates from a point. Parameters @@ -194,11 +198,13 @@ def point_to_polar(self, point: np.ndarray) -> tuple[float, float]: x, y = self.point_to_coords(point) return np.sqrt(x**2 + y**2), np.arctan2(y, x) - def c2p(self, *coords): + def c2p( + self, *coords: float | Sequence[float] | Sequence[Sequence[float]] | np.ndarray + ) -> np.ndarray: """Abbreviation for :meth:`coords_to_point`""" return self.coords_to_point(*coords) - def p2c(self, point): + def p2c(self, point: Point3D): """Abbreviation for :meth:`point_to_coords`""" return self.point_to_coords(point) @@ -213,7 +219,7 @@ def pt2pr(self, point: np.ndarray) -> tuple[float, float]: def get_axes(self): raise NotImplementedError() - def get_axis(self, index): + def get_axis(self, index: int) -> Mobject: return self.get_axes()[index] def get_origin(self) -> np.ndarray: @@ -226,19 +232,19 @@ def get_origin(self) -> np.ndarray: """ return self.coords_to_point(0, 0) - def get_x_axis(self): + def get_x_axis(self) -> Mobject: return self.get_axis(0) - def get_y_axis(self): + def get_y_axis(self) -> Mobject: return self.get_axis(1) - def get_z_axis(self): + def get_z_axis(self) -> Mobject: return self.get_axis(2) - def get_x_unit_size(self): + def get_x_unit_size(self) -> float: return self.get_x_axis().get_unit_size() - def get_y_unit_size(self): + def get_y_unit_size(self) -> float: return self.get_y_axis().get_unit_size() def get_x_axis_label( @@ -291,7 +297,7 @@ def get_y_axis_label( direction: Sequence[float] = UP * 0.5 + RIGHT, buff: float = SMALL_BUFF, **kwargs, - ): + ) -> Mobject: """Generate a y-axis label. Parameters @@ -370,9 +376,9 @@ def get_axis_labels(self): def add_coordinates( self, - *axes_numbers: (Iterable[float] | None | dict[float, str | float | Mobject]), - **kwargs, - ): + *axes_numbers: Iterable[float] | None | dict[float, str | float | Mobject], + **kwargs: Any, + ) -> Self: """Adds labels to the axes. Use ``Axes.coordinate_labels`` to access the coordinates after creation. @@ -426,15 +432,39 @@ def add_coordinates( return self + # overload necessary until https://github.com/python/mypy/issues/3737 is supported + @overload def get_line_from_axis_to_point( self, index: int, point: Sequence[float], - line_func: Line = DashedLine, - line_config: dict | None = None, - color: ParsableManimColor | None = None, - stroke_width: float = 2, - ) -> Line: + line_config: dict | None = ..., + color: ParsableManimColor | None = ..., + stroke_width: float = ..., + ) -> DashedLine: + ... + + @overload + def get_line_from_axis_to_point( + self, + index: int, + point: Sequence[float], + line_func: type[LineType], + line_config: dict | None = ..., + color: ParsableManimColor | None = ..., + stroke_width: float = ..., + ) -> LineType: + ... + + def get_line_from_axis_to_point( # type: ignore[no-untyped-def] + self, + index, + point, + line_func=DashedLine, + line_config=None, + color=None, + stroke_width=2, + ): """Returns a straight line from a given axis to a point in the scene. Parameters @@ -475,7 +505,7 @@ def get_line_from_axis_to_point( line = line_func(axis.get_projection(point), point, **line_config) return line - def get_vertical_line(self, point: Sequence[float], **kwargs) -> Line: + def get_vertical_line(self, point: Sequence[float], **kwargs: Any) -> Line: """A vertical line from the x-axis to a given point in the scene. Parameters @@ -589,8 +619,8 @@ def plot( function: Callable[[float], float], x_range: Sequence[float] | None = None, use_vectorized: bool = False, - **kwargs, - ): + **kwargs: Any, + ) -> ParametricFunction: """Generates a curve based on a function. Parameters @@ -685,10 +715,10 @@ def log_func(x): def plot_implicit_curve( self, - func: Callable, + func: Callable[[float, float], float], min_depth: int = 5, max_quads: int = 1500, - **kwargs, + **kwargs: Any, ) -> ImplicitFunction: """Creates the curves of an implicit function. @@ -737,7 +767,7 @@ def plot_parametric_curve( self, function: Callable[[float], np.ndarray], use_vectorized: bool = False, - **kwargs, + **kwargs: Any, ) -> ParametricFunction: """A parametric curve. @@ -784,8 +814,8 @@ def construct(self): def plot_polar_graph( self, r_func: Callable[[float], float], - theta_range: Sequence[float] = [0, 2 * PI], - **kwargs, + theta_range: Sequence[float] | None = None, + **kwargs: Any, ) -> ParametricFunction: """A polar graph. @@ -811,6 +841,7 @@ def construct(self): graph = plane.plot_polar_graph(r, [0, 2 * PI], color=ORANGE) self.add(plane, graph) """ + theta_range = theta_range if theta_range is not None else [0, 2 * PI] graph = ParametricFunction( function=lambda th: self.pr2pt(r_func(th), th), t_range=theta_range, @@ -828,8 +859,8 @@ def plot_surface( | Sequence[tuple[ParsableManimColor, float]] | None = None, colorscale_axis: int = 2, - **kwargs, - ): + **kwargs: Any, + ) -> Surface | OpenGLSurface: """Generates a surface based on a function. Parameters @@ -964,7 +995,9 @@ def construct(self): f"x={x} not located in the range of the graph ([{self.p2c(graph.get_start())[0]}, {self.p2c(graph.get_end())[0]}])", ) - def input_to_graph_coords(self, x: float, graph: ParametricFunction) -> tuple: + def input_to_graph_coords( + self, x: float, graph: ParametricFunction + ) -> tuple[float, float]: """Returns a tuple of the axis relative coordinates of the point on the graph based on the x-value given. @@ -980,7 +1013,7 @@ def input_to_graph_coords(self, x: float, graph: ParametricFunction) -> tuple: """ return x, graph.underlying_function(x) - def i2gc(self, x: float, graph: ParametricFunction) -> tuple: + def i2gc(self, x: float, graph: ParametricFunction) -> tuple[float, float]: """Alias for :meth:`input_to_graph_coords`.""" return self.input_to_graph_coords(x, graph) @@ -997,7 +1030,7 @@ def get_graph_label( buff: float = MED_SMALL_BUFF, color: ParsableManimColor | None = None, dot: bool = False, - dot_config: dict | None = None, + dot_config: dict[str, Any] | None = None, ) -> Mobject: """Creates a properly positioned label for the passed graph, with an optional dot. @@ -1242,8 +1275,8 @@ def get_area( color: ParsableManimColor | Iterable[ParsableManimColor] = (BLUE, GREEN), opacity: float = 0.3, bounded_graph: ParametricFunction = None, - **kwargs, - ): + **kwargs: Any, + ) -> Polygon: """Returns a :class:`~.Polygon` representing the area under the graph passed. Parameters @@ -1359,7 +1392,9 @@ def angle_of_tangent( p1 = np.array([*self.input_to_graph_coords(x + dx, graph)]) return angle_of_vector(p1 - p0) - def slope_of_tangent(self, x: float, graph: ParametricFunction, **kwargs) -> float: + def slope_of_tangent( + self, x: float, graph: ParametricFunction, **kwargs: Any + ) -> float: """Returns the slope of the tangent to the plotted curve at a particular x-value. @@ -1437,8 +1472,8 @@ def plot_antiderivative_graph( y_intercept: float = 0, samples: int = 50, use_vectorized: bool = False, - **kwargs, - ): + **kwargs: Any, + ) -> ParametricFunction: """Plots an antiderivative graph. Parameters @@ -1619,7 +1654,7 @@ def get_vertical_lines_to_graph( graph: ParametricFunction, x_range: Sequence[float] | None = None, num_lines: int = 20, - **kwargs, + **kwargs: Any, ) -> VGroup: """Obtains multiple lines from the x-axis to the curve. @@ -1678,7 +1713,7 @@ def get_T_label( label_color: ParsableManimColor | None = None, triangle_size: float = MED_SMALL_BUFF, triangle_color: ParsableManimColor | None = WHITE, - line_func: Line = Line, + line_func: type[Line] = Line, line_color: ParsableManimColor = YELLOW, ) -> VGroup: """Creates a labelled triangle marker with a vertical line from the x-axis @@ -1811,8 +1846,8 @@ def __init__( x_axis_config: dict | None = None, y_axis_config: dict | None = None, tips: bool = True, - **kwargs, - ): + **kwargs: Any, + ) -> None: VGroup.__init__(self, **kwargs) CoordinateSystem.__init__(self, x_range, y_range, x_length, y_length) @@ -1879,7 +1914,7 @@ def __init__( @staticmethod def _update_default_configs( default_configs: tuple[dict[Any, Any]], passed_configs: tuple[dict[Any, Any]] - ): + ) -> None: """Takes in two tuples of dicts and return modifies the first such that values from ``passed_configs`` overwrite values in ``default_configs``. If a key does not exist in default_configs, it is added to the dict. @@ -1912,7 +1947,7 @@ def _update_default_configs( def _create_axis( self, range_terms: Sequence[float], - axis_config: dict, + axis_config: dict[str, Any], length: float, ) -> NumberLine: """Creates an axis and dynamically adjusts its position depending on where 0 is located on the line. @@ -2161,8 +2196,8 @@ def plot_line_graph( line_color: ParsableManimColor = YELLOW, add_vertex_dots: bool = True, vertex_dot_radius: float = DEFAULT_DOT_RADIUS, - vertex_dot_style: dict | None = None, - **kwargs, + vertex_dot_style: dict[str, Any] | None = None, + **kwargs: Any, ) -> VDict: """Draws a line graph. @@ -2304,15 +2339,15 @@ def __init__( x_length: float | None = config.frame_height + 2.5, y_length: float | None = config.frame_height + 2.5, z_length: float | None = config.frame_height - 1.5, - z_axis_config: dict | None = None, - z_normal: Sequence[float] = DOWN, + z_axis_config: dict[str, Any] | None = None, + z_normal: Vector3 = DOWN, num_axis_pieces: int = 20, light_source: Sequence[float] = 9 * DOWN + 7 * LEFT + 10 * OUT, # opengl stuff (?) depth=None, gloss=0.5, - **kwargs, - ): + **kwargs: dict[str, Any], + ) -> None: super().__init__( x_range=x_range, x_length=x_length, @@ -2368,14 +2403,14 @@ def __init__( self._add_3d_pieces() self._set_axis_shading() - def _add_3d_pieces(self): + def _add_3d_pieces(self) -> None: for axis in self.axes: axis.pieces = VGroup(*axis.get_pieces(self.num_axis_pieces)) axis.add(axis.pieces) axis.set_stroke(width=0, family=False) axis.set_shade_in_3d(True) - def _set_axis_shading(self): + def _set_axis_shading(self) -> None: def make_func(axis): vect = self.light_source return lambda: ( @@ -2395,8 +2430,8 @@ def get_y_axis_label( edge: Sequence[float] = UR, direction: Sequence[float] = UR, buff: float = SMALL_BUFF, - rotation=PI / 2, - rotation_axis=OUT, + rotation: float = PI / 2, + rotation_axis: Vector3 = OUT, **kwargs, ) -> Mobject: """Generate a y-axis label. @@ -2443,12 +2478,12 @@ def construct(self): def get_z_axis_label( self, label: float | str | Mobject, - edge: Sequence[float] = OUT, - direction: Sequence[float] = RIGHT, + edge: Vector3 = OUT, + direction: Vector3 = RIGHT, buff: float = SMALL_BUFF, - rotation=PI / 2, - rotation_axis=RIGHT, - **kwargs, + rotation: float = PI / 2, + rotation_axis: Vector3 = RIGHT, + **kwargs: Any, ) -> Mobject: """Generate a z-axis label. @@ -2630,11 +2665,11 @@ def __init__( ), x_length: float | None = None, y_length: float | None = None, - background_line_style: dict | None = None, - faded_line_style: dict | None = None, + background_line_style: dict[str, Any] | None = None, + faded_line_style: dict[str, Any] | None = None, faded_line_ratio: int = 1, make_smooth_after_applying_functions: bool = True, - **kwargs, + **kwargs: dict[str, Any], ): # configs self.axis_config = { @@ -2679,7 +2714,7 @@ def __init__( self._init_background_lines() - def _init_background_lines(self): + def _init_background_lines(self) -> None: """Will init all the lines of NumberPlanes (faded or not)""" if self.faded_line_style is None: style = dict(self.background_line_style) @@ -2800,13 +2835,13 @@ def _get_lines_parallel_to_axis( lines2.add(new_line) return lines1, lines2 - def get_vector(self, coords: Sequence[float], **kwargs): + def get_vector(self, coords: Sequence[ManimFloat], **kwargs: Any) -> Arrow: kwargs["buff"] = 0 return Arrow( self.coords_to_point(0, 0), self.coords_to_point(*coords), **kwargs ) - def prepare_for_nonlinear_transform(self, num_inserted_curves: int = 50): + def prepare_for_nonlinear_transform(self, num_inserted_curves: int = 50) -> Self: for mob in self.family_members_with_points(): num_curves = mob.get_num_curves() if num_inserted_curves > num_curves: @@ -2900,13 +2935,13 @@ def __init__( azimuth_direction: str = "CCW", azimuth_label_buff: float = SMALL_BUFF, azimuth_label_font_size: float = 24, - radius_config: dict | None = None, - background_line_style: dict | None = None, - faded_line_style: dict | None = None, + radius_config: dict[str, Any] | None = None, + background_line_style: dict[str, Any] | None = None, + faded_line_style: dict[str, Any] | None = None, faded_line_ratio: int = 1, make_smooth_after_applying_functions: bool = True, - **kwargs, - ): + **kwargs: Any, + ) -> None: # error catching if azimuth_units in ["PI radians", "TAU radians", "degrees", "gradians", None]: self.azimuth_units = azimuth_units @@ -2977,7 +3012,7 @@ def __init__( self._init_background_lines() - def _init_background_lines(self): + def _init_background_lines(self) -> None: """Will init all the lines of NumberPlanes (faded or not)""" if self.faded_line_style is None: style = dict(self.background_line_style) @@ -3057,13 +3092,13 @@ def get_axes(self) -> VGroup: """ return self.axes - def get_vector(self, coords, **kwargs): + def get_vector(self, coords: Sequence[ManimFloat], **kwargs: Any) -> Arrow: kwargs["buff"] = 0 return Arrow( self.coords_to_point(0, 0), self.coords_to_point(*coords), **kwargs ) - def prepare_for_nonlinear_transform(self, num_inserted_curves=50): + def prepare_for_nonlinear_transform(self, num_inserted_curves: int = 50) -> Self: for mob in self.family_members_with_points(): num_curves = mob.get_num_curves() if num_inserted_curves > num_curves: @@ -3074,7 +3109,7 @@ def get_coordinate_labels( self, r_values: Iterable[float] | None = None, a_values: Iterable[float] | None = None, - **kwargs, + **kwargs: Any, ) -> VDict: """Gets labels for the coordinates @@ -3176,7 +3211,7 @@ def add_coordinates( self, r_values: Iterable[float] | None = None, a_values: Iterable[float] | None = None, - ): + ) -> Self: """Adds the coordinates. Parameters @@ -3189,7 +3224,7 @@ def add_coordinates( self.add(self.get_coordinate_labels(r_values, a_values)) return self - def get_radian_label(self, number, font_size=24, **kwargs): + def get_radian_label(self, number, font_size: float = 24, **kwargs: Any) -> MathTex: constant_label = {"PI radians": r"\pi", "TAU radians": r"\tau"}[ self.azimuth_units ] @@ -3258,7 +3293,7 @@ def construct(self): """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any) -> None: super().__init__( **kwargs, ) @@ -3284,7 +3319,7 @@ def n2p(self, number: float | complex) -> np.ndarray: """Abbreviation for :meth:`number_to_point`.""" return self.number_to_point(number) - def point_to_number(self, point: Sequence[float]) -> complex: + def point_to_number(self, point: Point3D) -> complex: """Accepts a point and returns a complex number equivalent to that point on the plane. Parameters @@ -3301,7 +3336,7 @@ def point_to_number(self, point: Sequence[float]) -> complex: x, y = self.point_to_coords(point) return complex(x, y) - def p2n(self, point: Sequence[float]) -> complex: + def p2n(self, point: Point3D) -> complex: """Abbreviation for :meth:`point_to_number`.""" return self.point_to_number(point) @@ -3319,7 +3354,7 @@ def _get_default_coordinate_values(self) -> list[float | complex]: return [*x_numbers, *y_numbers] def get_coordinate_labels( - self, *numbers: Iterable[float | complex], **kwargs + self, *numbers: Iterable[float | complex], **kwargs: Any ) -> VGroup: """Generates the :class:`~.DecimalNumber` mobjects for the coordinates of the plane. @@ -3354,7 +3389,9 @@ def get_coordinate_labels( self.coordinate_labels.add(number_mob) return self.coordinate_labels - def add_coordinates(self, *numbers: Iterable[float | complex], **kwargs): + def add_coordinates( + self, *numbers: Iterable[float | complex], **kwargs: Any + ) -> Self: """Adds the labels produced from :meth:`~.NumberPlane.get_coordinate_labels` to the plane. Parameters diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 72faf99ea1..1afa091cca 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -7,29 +7,18 @@ import copy import itertools as it +import math import operator as op import random import sys import types import warnings from functools import partialmethod, reduce -from math import ceil from pathlib import Path -from typing import ( - TYPE_CHECKING, - Callable, - Dict, - Iterable, - List, - Optional, - Sequence, - Tuple, - Type, - TypeVar, - Union, -) +from typing import TYPE_CHECKING, Callable, Iterable, Literal, TypeVar, Union import numpy as np +from typing_extensions import Self, TypeAlias from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL @@ -52,10 +41,25 @@ # TODO: Explain array_attrs -Updater = Union[Callable[["Mobject"], None], Callable[["Mobject", float], None]] +TimeBasedUpdater: TypeAlias = Callable[["Mobject", float], None] +NonTimeBasedUpdater: TypeAlias = Callable[["Mobject"], None] +Updater: TypeAlias = Union[NonTimeBasedUpdater, TimeBasedUpdater] T = TypeVar("T", bound="Mobject") if TYPE_CHECKING: + from manim.typing import ( + FunctionOverride, + Image, + ManimFloat, + ManimInt, + MappingFunction, + PathFuncType, + Point3D, + Point3D_Array, + Vector, + Vector3, + ) + from ..animation.animation import Animation @@ -82,12 +86,12 @@ class Mobject: animation_overrides = {} @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs) -> None: super().__init_subclass__(**kwargs) cls.animation_overrides: dict[ type[Animation], - Callable[[Mobject], Animation], + FunctionOverride, ] = {} cls._add_intrinsic_animation_overrides() cls._original__init__ = cls.__init__ @@ -95,20 +99,20 @@ def __init_subclass__(cls, **kwargs): def __init__( self, color: ParsableManimColor | list[ParsableManimColor] = WHITE, - name=None, - dim=3, + name: str | None = None, + dim: int = 3, target=None, - z_index=0, - ): + z_index: float = 0, + ) -> None: self.name = self.__class__.__name__ if name is None else name self.dim = dim self.target = target self.z_index = z_index self.point_hash = None self.submobjects = [] - self.updaters = [] + self.updaters: list[Updater] = [] self.updating_suspended = False - self.color: ManimColor = ManimColor.parse(color) + self.color = ManimColor.parse(color) self.reset_points() self.generate_points() @@ -118,7 +122,7 @@ def __init__( def animation_override_for( cls, animation_class: type[Animation], - ) -> Callable[[Mobject, ...], Animation] | None: + ) -> FunctionOverride | None: """Returns the function defining a specific animation override for this class. Parameters @@ -138,7 +142,7 @@ def animation_override_for( return None @classmethod - def _add_intrinsic_animation_overrides(cls): + def _add_intrinsic_animation_overrides(cls) -> None: """Initializes animation overrides marked with the :func:`~.override_animation` decorator. """ @@ -156,8 +160,8 @@ def _add_intrinsic_animation_overrides(cls): def add_animation_override( cls, animation_class: type[Animation], - override_func: Callable[[Mobject, ...], Animation], - ): + override_func: FunctionOverride, + ) -> None: """Add an animation override. This does not apply to subclasses. @@ -186,7 +190,7 @@ def add_animation_override( ) @classmethod - def set_default(cls, **kwargs): + def set_default(cls, **kwargs) -> None: """Sets the default values of keyword arguments. If this method is called without any additional keyword @@ -331,7 +335,7 @@ def construct(self): """ return _AnimationBuilder(self) - def __deepcopy__(self, clone_from_id): + def __deepcopy__(self, clone_from_id) -> Self: cls = self.__class__ result = cls.__new__(cls) clone_from_id[id(self)] = result @@ -340,30 +344,28 @@ def __deepcopy__(self, clone_from_id): result.original_id = str(id(self)) return result - def __repr__(self): + def __repr__(self) -> str: return str(self.name) - def reset_points(self): + def reset_points(self) -> None: """Sets :attr:`points` to be an empty array.""" self.points = np.zeros((0, self.dim)) - def init_colors(self): + def init_colors(self) -> None: """Initializes the colors. Gets called upon creation. This is an empty method that can be implemented by subclasses. """ - pass - def generate_points(self): + def generate_points(self) -> None: """Initializes :attr:`points` and therefore the shape. Gets called upon creation. This is an empty method that can be implemented by subclasses. """ - pass - def add(self, *mobjects: Mobject): + def add(self, *mobjects: Mobject) -> Self: """Add mobjects as submobjects. The mobjects are added to :attr:`submobjects`. @@ -449,7 +451,7 @@ def add(self, *mobjects: Mobject): self.submobjects = list_update(self.submobjects, unique_mobjects) return self - def insert(self, index: int, mobject: Mobject): + def insert(self, index: int, mobject: Mobject) -> None: """Inserts a mobject at a specific position into self.submobjects Effectively just calls ``self.submobjects.insert(index, mobject)``, @@ -470,13 +472,13 @@ def insert(self, index: int, mobject: Mobject): raise ValueError("Mobject cannot contain self") self.submobjects.insert(index, mobject) - def __add__(self, mobject): + def __add__(self, mobject: Mobject): raise NotImplementedError - def __iadd__(self, mobject): + def __iadd__(self, mobject: Mobject): raise NotImplementedError - def add_to_back(self, *mobjects: Mobject): + def add_to_back(self, *mobjects: Mobject) -> Self: """Add all passed mobjects to the back of the submobjects. If :attr:`submobjects` already contains the given mobjects, they just get moved @@ -532,7 +534,7 @@ def add_to_back(self, *mobjects: Mobject): self.submobjects = list(dict.fromkeys(mobjects)) + self.submobjects return self - def remove(self, *mobjects: Mobject): + def remove(self, *mobjects: Mobject) -> Self: """Remove :attr:`submobjects`. The mobjects are removed from :attr:`submobjects`, if they exist. @@ -565,7 +567,7 @@ def __sub__(self, other): def __isub__(self, other): raise NotImplementedError - def set(self, **kwargs): + def set(self, **kwargs) -> Self: """Sets attributes. I.e. ``my_mobject.set(foo=1)`` applies ``my_mobject.foo = 1``. @@ -622,7 +624,7 @@ def set(self, **kwargs): return self - def __getattr__(self, attr): + def __getattr__(self, attr: str) -> types.MethodType: # Add automatic compatibility layer # between properties and get_* and set_* # methods. @@ -670,7 +672,7 @@ def setter(self, value): raise AttributeError(f"{type(self).__name__} object has no attribute '{attr}'") @property - def width(self): + def width(self) -> float: """The width of the mobject. Returns @@ -703,11 +705,11 @@ def construct(self): return self.length_over_dim(0) @width.setter - def width(self, value): + def width(self, value: float): self.scale_to_fit_width(value) @property - def height(self): + def height(self) -> float: """The height of the mobject. Returns @@ -740,11 +742,11 @@ def construct(self): return self.length_over_dim(1) @height.setter - def height(self, value): + def height(self, value: float): self.scale_to_fit_height(value) @property - def depth(self): + def depth(self) -> float: """The depth of the mobject. Returns @@ -761,20 +763,21 @@ def depth(self): return self.length_over_dim(2) @depth.setter - def depth(self, value): + def depth(self, value: float): self.scale_to_fit_depth(value) - def get_array_attrs(self): + # Can't be staticmethod because of point_cloud_mobject.py + def get_array_attrs(self) -> list[Literal["points"]]: return ["points"] - def apply_over_attr_arrays(self, func): + def apply_over_attr_arrays(self, func: MappingFunction) -> Self: for attr in self.get_array_attrs(): setattr(self, attr, func(getattr(self, attr))) return self # Displaying - def get_image(self, camera=None): + def get_image(self, camera=None) -> Image: if camera is None: from ..camera.camera import Camera @@ -782,17 +785,17 @@ def get_image(self, camera=None): camera.capture_mobject(self) return camera.get_image() - def show(self, camera=None): + def show(self, camera=None) -> None: self.get_image(camera=camera).show() - def save_image(self, name=None): + def save_image(self, name: str | None = None) -> None: """Saves an image of only this :class:`Mobject` at its position to a png file.""" self.get_image().save( Path(config.get_dir("video_dir")).joinpath((name or str(self)) + ".png"), ) - def copy(self: T) -> T: + def copy(self) -> Self: """Create and return an identical copy of the :class:`Mobject` including all :attr:`submobjects`. @@ -807,7 +810,7 @@ def copy(self: T) -> T: """ return copy.deepcopy(self) - def generate_target(self, use_deepcopy=False): + def generate_target(self, use_deepcopy: bool = False) -> Self: self.target = None # Prevent unbounded linear recursion if use_deepcopy: self.target = copy.deepcopy(self) @@ -817,7 +820,7 @@ def generate_target(self, use_deepcopy=False): # Updating - def update(self, dt: float = 0, recursive: bool = True): + def update(self, dt: float = 0, recursive: bool = True) -> Self: """Apply all updaters. Does nothing if updating is suspended. @@ -854,7 +857,7 @@ def update(self, dt: float = 0, recursive: bool = True): submob.update(dt, recursive) return self - def get_time_based_updaters(self) -> list[Updater]: + def get_time_based_updaters(self) -> list[TimeBasedUpdater]: """Return all updaters using the ``dt`` parameter. The updaters use this parameter as the input for difference in time. @@ -904,7 +907,7 @@ def get_updaters(self) -> list[Updater]: """ return self.updaters - def get_family_updaters(self): + def get_family_updaters(self) -> list[Updater]: return list(it.chain(*(sm.get_updaters() for sm in self.get_family()))) def add_updater( @@ -912,7 +915,7 @@ def add_updater( update_function: Updater, index: int | None = None, call_updater: bool = False, - ): + ) -> Self: """Add an update function to this mobject. Update functions, or updaters in short, are functions that are applied to the @@ -986,7 +989,7 @@ def construct(self): update_function(self) return self - def remove_updater(self, update_function: Updater): + def remove_updater(self, update_function: Updater) -> Self: """Remove an updater. If the same updater is applied multiple times, every instance gets removed. @@ -1013,7 +1016,7 @@ def remove_updater(self, update_function: Updater): self.updaters.remove(update_function) return self - def clear_updaters(self, recursive: bool = True): + def clear_updaters(self, recursive: bool = True) -> Self: """Remove every updater. Parameters @@ -1039,7 +1042,7 @@ def clear_updaters(self, recursive: bool = True): submob.clear_updaters() return self - def match_updaters(self, mobject: Mobject): + def match_updaters(self, mobject: Mobject) -> Self: """Match the updaters of the given mobject. Parameters @@ -1069,7 +1072,7 @@ def match_updaters(self, mobject: Mobject): self.add_updater(updater) return self - def suspend_updating(self, recursive: bool = True): + def suspend_updating(self, recursive: bool = True) -> Self: """Disable updating from updaters and animations. @@ -1096,7 +1099,7 @@ def suspend_updating(self, recursive: bool = True): submob.suspend_updating(recursive) return self - def resume_updating(self, recursive: bool = True): + def resume_updating(self, recursive: bool = True) -> Self: """Enable updating from updaters and animations. Parameters @@ -1124,7 +1127,7 @@ def resume_updating(self, recursive: bool = True): # Transforming operations - def apply_to_family(self, func: Callable[[Mobject], None]): + def apply_to_family(self, func: Callable[[Mobject], None]) -> None: """Apply a function to ``self`` and every submobject with points recursively. Parameters @@ -1146,7 +1149,7 @@ def apply_to_family(self, func: Callable[[Mobject], None]): for mob in self.family_members_with_points(): func(mob) - def shift(self, *vectors: np.ndarray): + def shift(self, *vectors: Vector3) -> Self: """Shift by the given vectors. Parameters @@ -1172,7 +1175,7 @@ def shift(self, *vectors: np.ndarray): return self - def scale(self, scale_factor: float, **kwargs): + def scale(self, scale_factor: float, **kwargs) -> Self: r"""Scale the size by a factor. Default behavior is to scale about the center of the mobject. @@ -1218,17 +1221,17 @@ def construct(self): ) return self - def rotate_about_origin(self, angle, axis=OUT, axes=[]): + def rotate_about_origin(self, angle: float, axis: Vector3 = OUT, axes=[]) -> Self: """Rotates the :class:`~.Mobject` about the ORIGIN, which is at [0,0,0].""" return self.rotate(angle, axis, about_point=ORIGIN) def rotate( self, - angle, - axis=OUT, - about_point: Sequence[float] | None = None, + angle: float, + axis: Vector3 = OUT, + about_point: Point3D | None = None, **kwargs, - ): + ) -> Self: """Rotates the :class:`~.Mobject` about a certain point.""" rot_matrix = rotation_matrix(angle, axis) self.apply_points_function_about_point( @@ -1236,7 +1239,7 @@ def rotate( ) return self - def flip(self, axis=UP, **kwargs): + def flip(self, axis: Vector3 = UP, **kwargs) -> Self: """Flips/Mirrors an mobject about its center. Examples @@ -1255,7 +1258,7 @@ def construct(self): """ return self.rotate(TAU / 2, axis, **kwargs) - def stretch(self, factor, dim, **kwargs): + def stretch(self, factor: float, dim: int, **kwargs) -> Self: def func(points): points[:, dim] *= factor return points @@ -1263,7 +1266,7 @@ def func(points): self.apply_points_function_about_point(func, **kwargs) return self - def apply_function(self, function, **kwargs): + def apply_function(self, function: MappingFunction, **kwargs) -> Self: # Default to applying matrix about the origin, not mobjects center if len(kwargs) == 0: kwargs["about_point"] = ORIGIN @@ -1272,16 +1275,16 @@ def apply_function(self, function, **kwargs): ) return self - def apply_function_to_position(self, function): + def apply_function_to_position(self, function: MappingFunction) -> Self: self.move_to(function(self.get_center())) return self - def apply_function_to_submobject_positions(self, function): + def apply_function_to_submobject_positions(self, function: MappingFunction) -> Self: for submob in self.submobjects: submob.apply_function_to_position(function) return self - def apply_matrix(self, matrix, **kwargs): + def apply_matrix(self, matrix, **kwargs) -> Self: # Default to applying matrix about the origin, not mobjects center if ("about_point" not in kwargs) and ("about_edge" not in kwargs): kwargs["about_point"] = ORIGIN @@ -1293,9 +1296,11 @@ def apply_matrix(self, matrix, **kwargs): ) return self - def apply_complex_function(self, function, **kwargs): + def apply_complex_function( + self, function: Callable[[complex], complex], **kwargs + ) -> Self: """Applies a complex function to a :class:`Mobject`. - The x and y coordinates correspond to the real and imaginary parts respectively. + The x and y Point3Ds correspond to the real and imaginary parts respectively. Example ------- @@ -1327,7 +1332,9 @@ def R3_func(point): return self.apply_function(R3_func) - def wag(self, direction=RIGHT, axis=DOWN, wag_factor=1.0): + def wag( + self, direction: Vector3 = RIGHT, axis: Vector3 = DOWN, wag_factor: float = 1.0 + ) -> Self: for mob in self.family_members_with_points(): alphas = np.dot(mob.points, np.transpose(axis)) alphas -= min(alphas) @@ -1339,12 +1346,12 @@ def wag(self, direction=RIGHT, axis=DOWN, wag_factor=1.0): ) return self - def reverse_points(self): + def reverse_points(self) -> Self: for mob in self.family_members_with_points(): mob.apply_over_attr_arrays(lambda arr: np.array(list(reversed(arr)))) return self - def repeat(self, count: int): + def repeat(self, count: int) -> Self: """This can make transition animations nicer""" def repeat_array(array): @@ -1360,10 +1367,10 @@ def repeat_array(array): def apply_points_function_about_point( self, - func, - about_point=None, + func: MappingFunction, + about_point: Point3D = None, about_edge=None, - ): + ) -> Self: if about_point is None: if about_edge is None: about_edge = ORIGIN @@ -1380,7 +1387,7 @@ def pose_at_angle(self, **kwargs): # Positioning methods - def center(self): + def center(self) -> Self: """Moves the center of the mobject to the center of the scene. Returns @@ -1391,7 +1398,9 @@ def center(self): self.shift(-self.get_center()) return self - def align_on_border(self, direction, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER): + def align_on_border( + self, direction: Vector3, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER + ) -> Self: """Direction just needs to be a vector pointing towards side or corner in the 2d plane. """ @@ -1406,23 +1415,27 @@ def align_on_border(self, direction, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER): self.shift(shift_val) return self - def to_corner(self, corner=LEFT + DOWN, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER): + def to_corner( + self, corner: Vector3 = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER + ) -> Self: return self.align_on_border(corner, buff) - def to_edge(self, edge=LEFT, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER): + def to_edge( + self, edge: Vector3 = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER + ) -> Self: return self.align_on_border(edge, buff) def next_to( self, - mobject_or_point, - direction=RIGHT, - buff=DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, - aligned_edge=ORIGIN, - submobject_to_align=None, - index_of_submobject_to_align=None, - coor_mask=np.array([1, 1, 1]), - ): - """Move this :class:`~.Mobject` next to another's :class:`~.Mobject` or coordinate. + mobject_or_point: Mobject | Point3D, + direction: Vector3 = RIGHT, + buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, + aligned_edge: Vector3 = ORIGIN, + submobject_to_align: Mobject | None = None, + index_of_submobject_to_align: int | None = None, + coor_mask: Vector3 = np.array([1, 1, 1]), + ) -> Self: + """Move this :class:`~.Mobject` next to another's :class:`~.Mobject` or Point3D. Examples -------- @@ -1461,7 +1474,7 @@ def construct(self): self.shift((target_point - point_to_align + buff * direction) * coor_mask) return self - def shift_onto_screen(self, **kwargs): + def shift_onto_screen(self, **kwargs) -> Self: space_lengths = [config["frame_x_radius"], config["frame_y_radius"]] for vect in UP, DOWN, LEFT, RIGHT: dim = np.argmax(np.abs(vect)) @@ -1483,10 +1496,12 @@ def is_off_screen(self): return True return False - def stretch_about_point(self, factor, dim, point): + def stretch_about_point(self, factor: float, dim: int, point: Point3D) -> Self: return self.stretch(factor, dim, about_point=point) - def rescale_to_fit(self, length, dim, stretch=False, **kwargs): + def rescale_to_fit( + self, length: float, dim: int, stretch: bool = False, **kwargs + ) -> Self: old_length = self.length_over_dim(dim) if old_length == 0: return self @@ -1496,7 +1511,7 @@ def rescale_to_fit(self, length, dim, stretch=False, **kwargs): self.scale(length / old_length, **kwargs) return self - def scale_to_fit_width(self, width, **kwargs): + def scale_to_fit_width(self, width: float, **kwargs) -> Self: """Scales the :class:`~.Mobject` to fit a width while keeping height/depth proportional. Returns @@ -1522,7 +1537,7 @@ def scale_to_fit_width(self, width, **kwargs): return self.rescale_to_fit(width, 0, stretch=False, **kwargs) - def stretch_to_fit_width(self, width, **kwargs): + def stretch_to_fit_width(self, width: float, **kwargs) -> Self: """Stretches the :class:`~.Mobject` to fit a width, not keeping height/depth proportional. Returns @@ -1548,7 +1563,7 @@ def stretch_to_fit_width(self, width, **kwargs): return self.rescale_to_fit(width, 0, stretch=True, **kwargs) - def scale_to_fit_height(self, height, **kwargs): + def scale_to_fit_height(self, height: float, **kwargs) -> Self: """Scales the :class:`~.Mobject` to fit a height while keeping width/depth proportional. Returns @@ -1574,7 +1589,7 @@ def scale_to_fit_height(self, height, **kwargs): return self.rescale_to_fit(height, 1, stretch=False, **kwargs) - def stretch_to_fit_height(self, height, **kwargs): + def stretch_to_fit_height(self, height: float, **kwargs) -> Self: """Stretches the :class:`~.Mobject` to fit a height, not keeping width/depth proportional. Returns @@ -1600,36 +1615,36 @@ def stretch_to_fit_height(self, height, **kwargs): return self.rescale_to_fit(height, 1, stretch=True, **kwargs) - def scale_to_fit_depth(self, depth, **kwargs): + def scale_to_fit_depth(self, depth: float, **kwargs) -> Self: """Scales the :class:`~.Mobject` to fit a depth while keeping width/height proportional.""" return self.rescale_to_fit(depth, 2, stretch=False, **kwargs) - def stretch_to_fit_depth(self, depth, **kwargs): + def stretch_to_fit_depth(self, depth: float, **kwargs) -> Self: """Stretches the :class:`~.Mobject` to fit a depth, not keeping width/height proportional.""" return self.rescale_to_fit(depth, 2, stretch=True, **kwargs) - def set_coord(self, value, dim, direction=ORIGIN): + def set_coord(self, value, dim: int, direction: Vector3 = ORIGIN) -> Self: curr = self.get_coord(dim, direction) shift_vect = np.zeros(self.dim) shift_vect[dim] = value - curr self.shift(shift_vect) return self - def set_x(self, x, direction=ORIGIN): + def set_x(self, x: float, direction: Vector3 = ORIGIN) -> Self: """Set x value of the center of the :class:`~.Mobject` (``int`` or ``float``)""" return self.set_coord(x, 0, direction) - def set_y(self, y, direction=ORIGIN): + def set_y(self, y: float, direction: Vector3 = ORIGIN) -> Self: """Set y value of the center of the :class:`~.Mobject` (``int`` or ``float``)""" return self.set_coord(y, 1, direction) - def set_z(self, z, direction=ORIGIN): + def set_z(self, z: float, direction: Vector3 = ORIGIN) -> Self: """Set z value of the center of the :class:`~.Mobject` (``int`` or ``float``)""" return self.set_coord(z, 2, direction) - def space_out_submobjects(self, factor=1.5, **kwargs): + def space_out_submobjects(self, factor: float = 1.5, **kwargs) -> Self: self.scale(factor, **kwargs) for submob in self.submobjects: submob.scale(1.0 / factor) @@ -1637,11 +1652,11 @@ def space_out_submobjects(self, factor=1.5, **kwargs): def move_to( self, - point_or_mobject, - aligned_edge=ORIGIN, - coor_mask=np.array([1, 1, 1]), - ): - """Move center of the :class:`~.Mobject` to certain coordinate.""" + point_or_mobject: Point3D | Mobject, + aligned_edge: Vector3 = ORIGIN, + coor_mask: Vector3 = np.array([1, 1, 1]), + ) -> Self: + """Move center of the :class:`~.Mobject` to certain Point3D.""" if isinstance(point_or_mobject, Mobject): target = point_or_mobject.get_critical_point(aligned_edge) else: @@ -1650,7 +1665,9 @@ def move_to( self.shift((target - point_to_align) * coor_mask) return self - def replace(self, mobject, dim_to_match=0, stretch=False): + def replace( + self, mobject: Mobject, dim_to_match: int = 0, stretch: bool = False + ) -> Self: if not mobject.get_num_points() and not mobject.submobjects: raise Warning("Attempting to replace mobject with no points") if stretch: @@ -1668,16 +1685,16 @@ def replace(self, mobject, dim_to_match=0, stretch=False): def surround( self, mobject: Mobject, - dim_to_match=0, - stretch=False, - buff=MED_SMALL_BUFF, - ): + dim_to_match: int = 0, + stretch: bool = False, + buff: float = MED_SMALL_BUFF, + ) -> Self: self.replace(mobject, dim_to_match, stretch) length = mobject.length_over_dim(dim_to_match) self.scale((length + buff) / length) return self - def put_start_and_end_on(self, start, end): + def put_start_and_end_on(self, start: Point3D, end: Point3D) -> Self: curr_start, curr_end = self.get_start_and_end() curr_vect = curr_end - curr_start if np.all(curr_vect == 0): @@ -1703,7 +1720,7 @@ def put_start_and_end_on(self, start, end): # Background rectangle def add_background_rectangle( self, color: ParsableManimColor | None = None, opacity: float = 0.75, **kwargs - ): + ) -> Self: """Add a BackgroundRectangle as submobject. The BackgroundRectangle is added behind other submobjects. @@ -1742,12 +1759,12 @@ def add_background_rectangle( self.add_to_back(self.background_rectangle) return self - def add_background_rectangle_to_submobjects(self, **kwargs): + def add_background_rectangle_to_submobjects(self, **kwargs) -> Self: for submobject in self.submobjects: submobject.add_background_rectangle(**kwargs) return self - def add_background_rectangle_to_family_members_with_points(self, **kwargs): + def add_background_rectangle_to_family_members_with_points(self, **kwargs) -> Self: for mob in self.family_members_with_points(): mob.add_background_rectangle(**kwargs) return self @@ -1756,7 +1773,7 @@ def add_background_rectangle_to_family_members_with_points(self, **kwargs): def set_color( self, color: ParsableManimColor = YELLOW_C, family: bool = True - ) -> Mobject: + ) -> Self: """Condition is function which takes in one arguments, (x, y, z). Here it just recurses to submobjects, but in subclasses this should be further implemented based on the the inner workings @@ -1769,27 +1786,26 @@ def set_color( self.color = ManimColor.parse(color) return self - def set_color_by_gradient(self, *colors: Iterable[ParsableManimColor]): - """Set the color of this mobject's submobjects along the specified - gradient. - + def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self: + """ Parameters ---------- colors - The colors to use for the gradient. Use like - ``set_color_by_gradient(RED, BLUE, GREEN)``. + The colors to use for the gradient. Use like `set_color_by_gradient(RED, BLUE, GREEN)`. + self.color = ManimColor.parse(color) + return self """ self.set_submobject_colors_by_gradient(*colors) return self def set_colors_by_radial_gradient( self, - center=None, + center: Point3D | None = None, radius: float = 1, inner_color: ParsableManimColor = WHITE, outer_color: ParsableManimColor = BLACK, - ): + ) -> Self: self.set_submobject_colors_by_radial_gradient( center, radius, @@ -1813,11 +1829,11 @@ def set_submobject_colors_by_gradient(self, *colors: Iterable[ParsableManimColor def set_submobject_colors_by_radial_gradient( self, - center=None, + center: Point3D | None = None, radius: float = 1, inner_color: ParsableManimColor = WHITE, outer_color: ParsableManimColor = BLACK, - ): + ) -> Self: if center is None: center = self.get_center() @@ -1829,11 +1845,13 @@ def set_submobject_colors_by_radial_gradient( return self - def to_original_color(self): + def to_original_color(self) -> Self: self.set_color(self.color) return self - def fade_to(self, color: ParsableManimColor, alpha: float, family: bool = True): + def fade_to( + self, color: ParsableManimColor, alpha: float, family: bool = True + ) -> Self: if self.get_num_points() > 0: new_color = interpolate_color(self.get_color(), color, alpha) self.set_color(new_color, family=False) @@ -1842,7 +1860,7 @@ def fade_to(self, color: ParsableManimColor, alpha: float, family: bool = True): submob.fade_to(color, alpha) return self - def fade(self, darkness: float = 0.5, family: bool = True): + def fade(self, darkness: float = 0.5, family: bool = True) -> Self: if family: for submob in self.submobjects: submob.fade(darkness, family) @@ -1854,7 +1872,7 @@ def get_color(self) -> ManimColor: ## - def save_state(self): + def save_state(self) -> Self: """Save the current state (position, color & size). Can be restored with :meth:`~.Mobject.restore`.""" if hasattr(self, "saved_state"): # Prevent exponential growth of data @@ -1863,14 +1881,14 @@ def save_state(self): return self - def restore(self): + def restore(self) -> Self: """Restores the state that was previously saved with :meth:`~.Mobject.save_state`.""" if not hasattr(self, "saved_state") or self.save_state is None: raise Exception("Trying to restore without having saved") self.become(self.saved_state) return self - def reduce_across_dimension(self, reduce_func, dim: int) -> float: + def reduce_across_dimension(self, reduce_func: Callable, dim: int): """Find the min or max value from a dimension across all points in this and submobjects.""" assert dim >= 0 and dim <= 2 if len(self.submobjects) == 0 and len(self.points) == 0: @@ -1894,14 +1912,14 @@ def reduce_across_dimension(self, reduce_func, dim: int) -> float: rv = reduce_func([value, rv]) return rv - def nonempty_submobjects(self): + def nonempty_submobjects(self) -> list[Self]: return [ submob for submob in self.submobjects if len(submob.submobjects) != 0 or len(submob.points) != 0 ] - def get_merged_array(self, array_attr) -> np.ndarray: + def get_merged_array(self, array_attr: str) -> np.ndarray: """Return all of a given attribute from this mobject and all submobjects. May contain duplicates; the order is in a depth-first (pre-order) @@ -1912,7 +1930,7 @@ def get_merged_array(self, array_attr) -> np.ndarray: result = np.append(result, submob.get_merged_array(array_attr), axis=0) return result - def get_all_points(self) -> np.ndarray: + def get_all_points(self) -> Point3D_Array: """Return all points from this mobject and all submobjects. May contain duplicates; the order is in a depth-first (pre-order) @@ -1922,13 +1940,15 @@ def get_all_points(self) -> np.ndarray: # Getters - def get_points_defining_boundary(self): + def get_points_defining_boundary(self) -> Point3D_Array: return self.get_all_points() - def get_num_points(self): + def get_num_points(self) -> int: return len(self.points) - def get_extremum_along_dim(self, points=None, dim=0, key=0): + def get_extremum_along_dim( + self, points: Point3D_Array | None = None, dim: int = 0, key: int = 0 + ) -> np.ndarray | float: if points is None: points = self.get_points_defining_boundary() values = points[:, dim] @@ -1939,7 +1959,7 @@ def get_extremum_along_dim(self, points=None, dim=0, key=0): else: return np.max(values) - def get_critical_point(self, direction): + def get_critical_point(self, direction: Vector3) -> Point3D: """Picture a box bounding the :class:`~.Mobject`. Such a box has 9 'critical points': 4 corners, 4 edge center, the center. This returns one of them, along the given direction. @@ -1968,28 +1988,28 @@ def get_critical_point(self, direction): # Pseudonyms for more general get_critical_point method - def get_edge_center(self, direction) -> np.ndarray: - """Get edge coordinates for certain direction.""" + def get_edge_center(self, direction: Vector3) -> Point3D: + """Get edge Point3Ds for certain direction.""" return self.get_critical_point(direction) - def get_corner(self, direction) -> np.ndarray: - """Get corner coordinates for certain direction.""" + def get_corner(self, direction: Vector3) -> Point3D: + """Get corner Point3Ds for certain direction.""" return self.get_critical_point(direction) - def get_center(self) -> np.ndarray: - """Get center coordinates""" + def get_center(self) -> Point3D: + """Get center Point3Ds""" return self.get_critical_point(np.zeros(self.dim)) - def get_center_of_mass(self): + def get_center_of_mass(self) -> Point3D: return np.apply_along_axis(np.mean, 0, self.get_all_points()) - def get_boundary_point(self, direction): + def get_boundary_point(self, direction: Vector3) -> Point3D: all_points = self.get_points_defining_boundary() index = np.argmax(np.dot(all_points, np.array(direction).T)) return all_points[index] - def get_midpoint(self) -> np.ndarray: - """Get coordinates of the middle of the path that forms the :class:`~.Mobject`. + def get_midpoint(self) -> Point3D: + """Get Point3Ds of the middle of the path that forms the :class:`~.Mobject`. Examples -------- @@ -2011,74 +2031,74 @@ def construct(self): """ return self.point_from_proportion(0.5) - def get_top(self) -> np.ndarray: - """Get top coordinates of a box bounding the :class:`~.Mobject`""" + def get_top(self) -> Point3D: + """Get top Point3Ds of a box bounding the :class:`~.Mobject`""" return self.get_edge_center(UP) - def get_bottom(self) -> np.ndarray: - """Get bottom coordinates of a box bounding the :class:`~.Mobject`""" + def get_bottom(self) -> Point3D: + """Get bottom Point3Ds of a box bounding the :class:`~.Mobject`""" return self.get_edge_center(DOWN) - def get_right(self) -> np.ndarray: - """Get right coordinates of a box bounding the :class:`~.Mobject`""" + def get_right(self) -> Point3D: + """Get right Point3Ds of a box bounding the :class:`~.Mobject`""" return self.get_edge_center(RIGHT) - def get_left(self) -> np.ndarray: - """Get left coordinates of a box bounding the :class:`~.Mobject`""" + def get_left(self) -> Point3D: + """Get left Point3Ds of a box bounding the :class:`~.Mobject`""" return self.get_edge_center(LEFT) - def get_zenith(self) -> np.ndarray: - """Get zenith coordinates of a box bounding a 3D :class:`~.Mobject`.""" + def get_zenith(self) -> Point3D: + """Get zenith Point3Ds of a box bounding a 3D :class:`~.Mobject`.""" return self.get_edge_center(OUT) - def get_nadir(self) -> np.ndarray: - """Get nadir (opposite the zenith) coordinates of a box bounding a 3D :class:`~.Mobject`.""" + def get_nadir(self) -> Point3D: + """Get nadir (opposite the zenith) Point3Ds of a box bounding a 3D :class:`~.Mobject`.""" return self.get_edge_center(IN) - def length_over_dim(self, dim): + def length_over_dim(self, dim: int) -> float: """Measure the length of an :class:`~.Mobject` in a certain direction.""" return self.reduce_across_dimension( max, dim, ) - self.reduce_across_dimension(min, dim) - def get_coord(self, dim, direction=ORIGIN): + def get_coord(self, dim: int, direction: Vector3 = ORIGIN): """Meant to generalize ``get_x``, ``get_y`` and ``get_z``""" return self.get_extremum_along_dim(dim=dim, key=direction[dim]) - def get_x(self, direction=ORIGIN) -> np.float64: - """Returns x coordinate of the center of the :class:`~.Mobject` as ``float``""" + def get_x(self, direction: Vector3 = ORIGIN) -> ManimFloat: + """Returns x Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(0, direction) - def get_y(self, direction=ORIGIN) -> np.float64: - """Returns y coordinate of the center of the :class:`~.Mobject` as ``float``""" + def get_y(self, direction: Vector3 = ORIGIN) -> ManimFloat: + """Returns y Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(1, direction) - def get_z(self, direction=ORIGIN) -> np.float64: - """Returns z coordinate of the center of the :class:`~.Mobject` as ``float``""" + def get_z(self, direction: Vector3 = ORIGIN) -> ManimFloat: + """Returns z Point3D of the center of the :class:`~.Mobject` as ``float``""" return self.get_coord(2, direction) - def get_start(self): + def get_start(self) -> Point3D: """Returns the point, where the stroke that surrounds the :class:`~.Mobject` starts.""" self.throw_error_if_no_points() return np.array(self.points[0]) - def get_end(self): + def get_end(self) -> Point3D: """Returns the point, where the stroke that surrounds the :class:`~.Mobject` ends.""" self.throw_error_if_no_points() return np.array(self.points[-1]) - def get_start_and_end(self): + def get_start_and_end(self) -> tuple[Point3D, Point3D]: """Returns starting and ending point of a stroke as a ``tuple``.""" return self.get_start(), self.get_end() - def point_from_proportion(self, alpha): + def point_from_proportion(self, alpha: float) -> Point3D: raise NotImplementedError("Please override in a child class.") - def proportion_from_point(self, point): + def proportion_from_point(self, point: Point3D) -> float: raise NotImplementedError("Please override in a child class.") - def get_pieces(self, n_pieces): + def get_pieces(self, n_pieces: float) -> Group: template = self.copy() template.submobjects = [] alphas = np.linspace(0, 1, n_pieces + 1) @@ -2089,7 +2109,7 @@ def get_pieces(self, n_pieces): ) ) - def get_z_index_reference_point(self): + def get_z_index_reference_point(self) -> Point3D: # TODO, better place to define default z_index_group? z_index_group = getattr(self, "z_index_group", self) return z_index_group.get_center() @@ -2104,51 +2124,53 @@ def has_no_points(self) -> bool: # Match other mobject properties - def match_color(self, mobject: Mobject): + def match_color(self, mobject: Mobject) -> Self: """Match the color with the color of another :class:`~.Mobject`.""" return self.set_color(mobject.get_color()) - def match_dim_size(self, mobject: Mobject, dim, **kwargs): + def match_dim_size(self, mobject: Mobject, dim: int, **kwargs) -> Self: """Match the specified dimension with the dimension of another :class:`~.Mobject`.""" return self.rescale_to_fit(mobject.length_over_dim(dim), dim, **kwargs) - def match_width(self, mobject: Mobject, **kwargs): + def match_width(self, mobject: Mobject, **kwargs) -> Self: """Match the width with the width of another :class:`~.Mobject`.""" return self.match_dim_size(mobject, 0, **kwargs) - def match_height(self, mobject: Mobject, **kwargs): + def match_height(self, mobject: Mobject, **kwargs) -> Self: """Match the height with the height of another :class:`~.Mobject`.""" return self.match_dim_size(mobject, 1, **kwargs) - def match_depth(self, mobject: Mobject, **kwargs): + def match_depth(self, mobject: Mobject, **kwargs) -> Self: """Match the depth with the depth of another :class:`~.Mobject`.""" return self.match_dim_size(mobject, 2, **kwargs) - def match_coord(self, mobject: Mobject, dim, direction=ORIGIN): - """Match the coordinates with the coordinates of another :class:`~.Mobject`.""" + def match_coord( + self, mobject: Mobject, dim: int, direction: Vector3 = ORIGIN + ) -> Self: + """Match the Point3Ds with the Point3Ds of another :class:`~.Mobject`.""" return self.set_coord( mobject.get_coord(dim, direction), dim=dim, direction=direction, ) - def match_x(self, mobject: Mobject, direction=ORIGIN): + def match_x(self, mobject: Mobject, direction=ORIGIN) -> Self: """Match x coord. to the x coord. of another :class:`~.Mobject`.""" return self.match_coord(mobject, 0, direction) - def match_y(self, mobject: Mobject, direction=ORIGIN): + def match_y(self, mobject: Mobject, direction=ORIGIN) -> Self: """Match y coord. to the x coord. of another :class:`~.Mobject`.""" return self.match_coord(mobject, 1, direction) - def match_z(self, mobject: Mobject, direction=ORIGIN): + def match_z(self, mobject: Mobject, direction=ORIGIN) -> Self: """Match z coord. to the x coord. of another :class:`~.Mobject`.""" return self.match_coord(mobject, 2, direction) def align_to( self, - mobject_or_point: Mobject | np.ndarray | list, - direction=ORIGIN, - ): + mobject_or_point: Mobject | Point3D, + direction: Vector3 = ORIGIN, + ) -> Self: """Aligns mobject to another :class:`~.Mobject` in a certain direction. Examples: @@ -2180,33 +2202,33 @@ def __iter__(self): def __len__(self): return len(self.split()) - def get_group_class(self): + def get_group_class(self) -> type[Group]: return Group @staticmethod - def get_mobject_type_class(): + def get_mobject_type_class() -> type[Mobject]: """Return the base class of this mobject type.""" return Mobject - def split(self): + def split(self) -> list[Self]: result = [self] if len(self.points) > 0 else [] return result + self.submobjects - def get_family(self, recurse=True): - sub_families = list(map(Mobject.get_family, self.submobjects)) + def get_family(self, recurse: bool = True) -> list[Self]: + sub_families = [x.get_family() for x in self.submobjects] all_mobjects = [self] + list(it.chain(*sub_families)) return remove_list_redundancies(all_mobjects) - def family_members_with_points(self) -> list[Mobject]: + def family_members_with_points(self) -> list[Self]: return [m for m in self.get_family() if m.get_num_points() > 0] def arrange( self, - direction: Sequence[float] = RIGHT, - buff=DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, - center=True, + direction: Vector3 = RIGHT, + buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, + center: bool = True, **kwargs, - ): + ) -> Self: """Sorts :class:`~.Mobject` next to each other on screen. Examples @@ -2235,14 +2257,14 @@ def arrange_in_grid( rows: int | None = None, cols: int | None = None, buff: float | tuple[float, float] = MED_SMALL_BUFF, - cell_alignment: np.ndarray = ORIGIN, + cell_alignment: Vector3 = ORIGIN, row_alignments: str | None = None, # "ucd" col_alignments: str | None = None, # "lcr" row_heights: Iterable[float | None] | None = None, col_widths: Iterable[float | None] | None = None, flow_order: str = "rd", **kwargs, - ): + ) -> Self: """Arrange submobjects in a grid. Parameters @@ -2348,15 +2370,15 @@ def init_size(num, alignments, sizes): # calculate rows cols if rows is None and cols is None: - cols = ceil(np.sqrt(len(mobs))) + cols = math.ceil(math.sqrt(len(mobs))) # make the grid as close to quadratic as possible. # choosing cols first can results in cols>rows. # This is favored over rows>cols since in general # the sceene is wider than high. if rows is None: - rows = ceil(len(mobs) / cols) + rows = math.ceil(len(mobs) / cols) if cols is None: - cols = ceil(len(mobs) / rows) + cols = math.ceil(len(mobs) / rows) if rows * cols < len(mobs): raise ValueError("Too few rows and columns to fit all submobjetcs.") # rows and cols are now finally valid. @@ -2471,24 +2493,28 @@ def init_sizes(sizes, num, measures, name): self.move_to(start_pos) return self - def sort(self, point_to_num_func=lambda p: p[0], submob_func=None): + def sort( + self, + point_to_num_func: Callable[[Point3D], ManimInt] = lambda p: p[0], + submob_func: Callable[[Mobject], ManimInt] | None = None, + ) -> Self: """Sorts the list of :attr:`submobjects` by a function defined by ``submob_func``.""" if submob_func is None: - def submob_func(m): + def submob_func(m: Mobject): return point_to_num_func(m.get_center()) self.submobjects.sort(key=submob_func) return self - def shuffle(self, recursive=False): + def shuffle(self, recursive: bool = False) -> None: """Shuffles the list of :attr:`submobjects`.""" if recursive: for submob in self.submobjects: submob.shuffle(recursive=True) random.shuffle(self.submobjects) - def invert(self, recursive=False): + def invert(self, recursive: bool = False) -> None: """Inverts the list of :attr:`submobjects`. Parameters @@ -2512,10 +2538,10 @@ def construct(self): if recursive: for submob in self.submobjects: submob.invert(recursive=True) - list.reverse(self.submobjects) + self.submobjects.reverse() # Just here to keep from breaking old scenes. - def arrange_submobjects(self, *args, **kwargs): + def arrange_submobjects(self, *args, **kwargs) -> Self: """Arrange the position of :attr:`submobjects` with a small buffer. Examples @@ -2536,11 +2562,11 @@ def construct(self): """ return self.arrange(*args, **kwargs) - def sort_submobjects(self, *args, **kwargs): + def sort_submobjects(self, *args, **kwargs) -> Self: """Sort the :attr:`submobjects`""" return self.sort(*args, **kwargs) - def shuffle_submobjects(self, *args, **kwargs): + def shuffle_submobjects(self, *args, **kwargs) -> None: """Shuffles the order of :attr:`submobjects` Examples @@ -2559,7 +2585,7 @@ def construct(self): return self.shuffle(*args, **kwargs) # Alignment - def align_data(self, mobject: Mobject, skip_point_alignment: bool = False): + def align_data(self, mobject: Mobject, skip_point_alignment: bool = False) -> None: """Aligns the data of this mobject with another mobject. Afterwards, the two mobjects will have the same number of submobjects @@ -2590,7 +2616,7 @@ def get_point_mobject(self, center=None): msg = f"get_point_mobject not implemented for {self.__class__.__name__}" raise NotImplementedError(msg) - def align_points(self, mobject): + def align_points(self, mobject: Mobject) -> Self: count1 = self.get_num_points() count2 = mobject.get_num_points() if count1 < count2: @@ -2599,10 +2625,10 @@ def align_points(self, mobject): mobject.align_points_with_larger(self) return self - def align_points_with_larger(self, larger_mobject): + def align_points_with_larger(self, larger_mobject: Mobject): raise NotImplementedError("Please override in a child class.") - def align_submobjects(self, mobject): + def align_submobjects(self, mobject: Mobject) -> Self: mob1 = self mob2 = mobject n1 = len(mob1.submobjects) @@ -2627,14 +2653,14 @@ def null_point_align(self, mobject: Mobject): m2.push_self_into_submobjects() return self - def push_self_into_submobjects(self): + def push_self_into_submobjects(self) -> Self: copy = self.copy() copy.submobjects = [] self.reset_points() self.add(copy) return self - def add_n_more_submobjects(self, n): + def add_n_more_submobjects(self, n: int) -> Self | None: if n == 0: return @@ -2657,10 +2683,16 @@ def add_n_more_submobjects(self, n): self.submobjects = new_submobs return self - def repeat_submobject(self, submob): + def repeat_submobject(self, submob: Mobject) -> Self: return submob.copy() - def interpolate(self, mobject1, mobject2, alpha, path_func=straight_path()): + def interpolate( + self, + mobject1: Mobject, + mobject2: Mobject, + alpha: float, + path_func: PathFuncType = straight_path(), + ) -> Self: """Turns this :class:`~.Mobject` into an interpolation between ``mobject1`` and ``mobject2``. @@ -2685,7 +2717,7 @@ def construct(self): self.interpolate_color(mobject1, mobject2, alpha) return self - def interpolate_color(self, mobject1, mobject2, alpha): + def interpolate_color(self, mobject1: Mobject, mobject2: Mobject, alpha: float): raise NotImplementedError("Please override in a child class.") def become( @@ -2697,7 +2729,7 @@ def become( match_depth: bool = False, match_center: bool = False, stretch: bool = False, - ): + ) -> Self: """Edit points, colors and submobjects to be identical to another :class:`~.Mobject` @@ -2754,7 +2786,7 @@ def construct(self): sm1.interpolate_color(sm1, sm2, 1) return self - def match_points(self, mobject: Mobject, copy_submobjects: bool = True): + def match_points(self, mobject: Mobject, copy_submobjects: bool = True) -> Self: """Edit points, positions, and submobjects to be identical to another :class:`~.Mobject`, while keeping the style unchanged. @@ -2776,7 +2808,7 @@ def construct(self): return self # Errors - def throw_error_if_no_points(self): + def throw_error_if_no_points(self) -> None: if self.has_no_points(): caller_name = sys._getframe(1).f_code.co_name raise Exception( @@ -2827,8 +2859,8 @@ def construct(self): self.z_index = z_index_value return self - def set_z_index_by_z_coordinate(self): - """Sets the :class:`~.Mobject`'s z coordinate to the value of :attr:`z_index`. + def set_z_index_by_z_Point3D(self) -> Self: + """Sets the :class:`~.Mobject`'s z Point3D to the value of :attr:`z_index`. Returns ------- @@ -2850,13 +2882,13 @@ class Group(Mobject, metaclass=ConvertToOpenGL): be added to the group. """ - def __init__(self, *mobjects, **kwargs): + def __init__(self, *mobjects, **kwargs) -> None: super().__init__(**kwargs) self.add(*mobjects) class _AnimationBuilder: - def __init__(self, mobject): + def __init__(self, mobject) -> None: self.mobject = mobject self.mobject.generate_target() @@ -2868,7 +2900,7 @@ def __init__(self, mobject): self.cannot_pass_args = False self.anim_args = {} - def __call__(self, **kwargs): + def __call__(self, **kwargs) -> Self: if self.cannot_pass_args: raise ValueError( "Animation arguments must be passed before accessing methods and can only be passed once", @@ -2879,7 +2911,7 @@ def __call__(self, **kwargs): return self - def __getattr__(self, method_name): + def __getattr__(self, method_name) -> types.MethodType: method = getattr(self.mobject.target, method_name) has_overridden_animation = hasattr(method, "_override_animate") @@ -2907,8 +2939,10 @@ def update_target(*method_args, **method_kwargs): return update_target - def build(self): - from ..animation.transform import _MethodAnimation + def build(self) -> Animation: + from ..animation.transform import ( # is this to prevent circular import? + _MethodAnimation, + ) if self.overridden_animation: anim = self.overridden_animation @@ -2921,7 +2955,7 @@ def build(self): return anim -def override_animate(method): +def override_animate(method) -> types.FunctionType: r"""Decorator for overriding method animations. This allows to specify a method (returning an :class:`~.Animation`) diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index c02ddd5c72..3425fdd9e8 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -13,8 +13,16 @@ from manim import config, logger from manim.constants import * +from manim.renderer.shader_wrapper import get_colormap_code from manim.utils.bezier import integer_interpolate, interpolate -from manim.utils.color import * +from manim.utils.color import ( + WHITE, + ManimColor, + ParsableManimColor, + color_gradient, + color_to_rgb, + rgb_to_hex, +) from manim.utils.config_ops import _Data, _Uniforms # from ..utils.iterables import batch_by_property diff --git a/manim/mobject/opengl/opengl_vectorized_mobject.py b/manim/mobject/opengl/opengl_vectorized_mobject.py index d486dc5c76..d0c62d893b 100644 --- a/manim/mobject/opengl/opengl_vectorized_mobject.py +++ b/manim/mobject/opengl/opengl_vectorized_mobject.py @@ -3,7 +3,7 @@ import itertools as it import operator as op from functools import reduce, wraps -from typing import Callable, Iterable, Optional, Sequence +from typing import Callable, Iterable, Sequence import moderngl import numpy as np @@ -22,7 +22,7 @@ proportions_along_bezier_curve_for_point, quadratic_bezier_remap, ) -from manim.utils.color import * +from manim.utils.color import BLACK, WHITE, ManimColor, ParsableManimColor from manim.utils.config_ops import _Data from manim.utils.iterables import listify, make_even, resize_with_interpolation from manim.utils.space_ops import ( @@ -136,6 +136,7 @@ def __init__( self.needs_new_triangulation = True self.triangulation = np.zeros(0, dtype="i4") self.orientation = 1 + self.fill_data = None self.stroke_data = None self.fill_shader_wrapper = None @@ -1283,14 +1284,17 @@ def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarra for _ in range(-diff): ipc[np.argmax(ipc)] -= 1 - new_points = [] + new_length = sum(x + 1 for x in ipc) + new_points = np.empty((new_length, nppc, 3)) + i = 0 for group, n_inserts in zip(bezier_groups, ipc): # What was once a single quadratic curve defined # by "group" will now be broken into n_inserts + 1 # smaller quadratic curves alphas = np.linspace(0, 1, n_inserts + 2) for a1, a2 in zip(alphas, alphas[1:]): - new_points += partial_quadratic_bezier_points(group, a1, a2) + new_points[i] = partial_quadratic_bezier_points(group, a1, a2) + i = i + 1 return np.vstack(new_points) def interpolate(self, mobject1, mobject2, alpha, *args, **kwargs): diff --git a/manim/mobject/text/tex_mobject.py b/manim/mobject/text/tex_mobject.py index dbdf6e3a50..47cbe6cd96 100644 --- a/manim/mobject/text/tex_mobject.py +++ b/manim/mobject/text/tex_mobject.py @@ -28,7 +28,7 @@ import re from functools import reduce from textwrap import dedent -from typing import Dict, Iterable, Optional +from typing import Iterable from manim import config, logger from manim.constants import * diff --git a/manim/mobject/text/text_mobject.py b/manim/mobject/text/text_mobject.py index 497f03e560..3a2b0bd3df 100644 --- a/manim/mobject/text/text_mobject.py +++ b/manim/mobject/text/text_mobject.py @@ -462,7 +462,7 @@ def __init__( t2g = kwargs.pop("text2gradient", t2g) t2s = kwargs.pop("text2slant", t2s) t2w = kwargs.pop("text2weight", t2w) - self.t2c = t2c + self.t2c = {k: ManimColor(v).to_hex() for k, v in t2c.items()} self.t2f = t2f self.t2g = t2g self.t2s = t2s @@ -482,7 +482,7 @@ def __init__( self.line_spacing = self._font_size + self._font_size * self.line_spacing color: ManimColor = ManimColor(color) if color else VMobject().color - file_name = self._text2svg(color) + file_name = self._text2svg(color.to_hex()) PangoUtils.remove_last_M(file_name) super().__init__( file_name, @@ -737,7 +737,7 @@ def _text2settings(self, color: str): # setting_args requires values to be strings default_args = { - arg: getattr(self, arg) if arg != "color" else str(color) for _, arg in t2xs + arg: getattr(self, arg) if arg != "color" else color for _, arg in t2xs } settings = self._get_settings_from_t2xs(t2xs, default_args) @@ -1307,7 +1307,7 @@ def font_size(self, font_val): else: self.scale(font_val / self.font_size) - def _text2hash(self, color: ManimColor): + def _text2hash(self, color: ParsableManimColor): """Generates ``sha256`` hash for file name.""" settings = ( "MARKUPPANGO" @@ -1324,8 +1324,9 @@ def _text2hash(self, color: ManimColor): hasher.update(id_str.encode()) return hasher.hexdigest()[:16] - def _text2svg(self, color: ManimColor): + def _text2svg(self, color: ParsableManimColor | None): """Convert the text to SVG using Pango.""" + color = ManimColor(color) size = self._font_size line_spacing = self.line_spacing size /= TEXT2SVG_ADJUSTMENT_FACTOR diff --git a/manim/mobject/three_d/three_d_utils.py b/manim/mobject/three_d/three_d_utils.py index 397e304af1..ec3af9ac05 100644 --- a/manim/mobject/three_d/three_d_utils.py +++ b/manim/mobject/three_d/three_d_utils.py @@ -14,40 +14,45 @@ ] +from typing import TYPE_CHECKING, Literal + import numpy as np from manim.constants import ORIGIN, UP from manim.utils.space_ops import get_unit_normal +if TYPE_CHECKING: + from manim.typing import Point3D, Vector + -def get_3d_vmob_gradient_start_and_end_points(vmob): +def get_3d_vmob_gradient_start_and_end_points(vmob) -> tuple[Point3D, Point3D]: return ( get_3d_vmob_start_corner(vmob), get_3d_vmob_end_corner(vmob), ) -def get_3d_vmob_start_corner_index(vmob): +def get_3d_vmob_start_corner_index(vmob) -> Literal[0]: return 0 -def get_3d_vmob_end_corner_index(vmob): +def get_3d_vmob_end_corner_index(vmob) -> int: return ((len(vmob.points) - 1) // 6) * 3 -def get_3d_vmob_start_corner(vmob): +def get_3d_vmob_start_corner(vmob) -> Point3D: if vmob.get_num_points() == 0: return np.array(ORIGIN) return vmob.points[get_3d_vmob_start_corner_index(vmob)] -def get_3d_vmob_end_corner(vmob): +def get_3d_vmob_end_corner(vmob) -> Point3D: if vmob.get_num_points() == 0: return np.array(ORIGIN) return vmob.points[get_3d_vmob_end_corner_index(vmob)] -def get_3d_vmob_unit_normal(vmob, point_index): +def get_3d_vmob_unit_normal(vmob, point_index: int) -> Vector: n_points = vmob.get_num_points() if len(vmob.get_anchors()) <= 2: return np.array(UP) @@ -63,9 +68,9 @@ def get_3d_vmob_unit_normal(vmob, point_index): return unit_normal -def get_3d_vmob_start_corner_unit_normal(vmob): +def get_3d_vmob_start_corner_unit_normal(vmob) -> Vector: return get_3d_vmob_unit_normal(vmob, get_3d_vmob_start_corner_index(vmob)) -def get_3d_vmob_end_corner_unit_normal(vmob): +def get_3d_vmob_end_corner_unit_normal(vmob) -> Vector: return get_3d_vmob_unit_normal(vmob, get_3d_vmob_end_corner_index(vmob)) diff --git a/manim/mobject/three_d/three_dimensions.py b/manim/mobject/three_d/three_dimensions.py index 1ff7df118d..1be9edf7dd 100644 --- a/manim/mobject/three_d/three_dimensions.py +++ b/manim/mobject/three_d/three_dimensions.py @@ -2,6 +2,9 @@ from __future__ import annotations +from manim.typing import Point3D, Vector3 +from manim.utils.color import BLUE, BLUE_D, BLUE_E, LIGHT_GREY, WHITE, interpolate_color + __all__ = [ "ThreeDVMobject", "Surface", @@ -16,9 +19,10 @@ "Torus", ] -from typing import Callable, Sequence +from typing import Any, Callable, Iterable, Sequence import numpy as np +from typing_extensions import Self from manim import config, logger from manim.constants import * @@ -113,7 +117,7 @@ def __init__( stroke_width: float = 0.5, should_make_jagged: bool = False, pre_function_handle_to_anchor_scale_factor: float = 0.00001, - **kwargs, + **kwargs: Any, ) -> None: self.u_range = u_range self.v_range = v_range @@ -141,16 +145,9 @@ def __init__( self.make_jagged() def func(self, u: float, v: float) -> np.ndarray: - """The z values defining the :class:`Surface` being plotted. - - Returns - ------- - :class:`numpy.array` - The z values defining the :class:`Surface`. - """ return self._func(u, v) - def _get_u_values_and_v_values(self): + def _get_u_values_and_v_values(self) -> tuple[np.ndarray, np.ndarray]: res = tuplify(self.resolution) if len(res) == 1: u_res = v_res = res[0] @@ -162,7 +159,7 @@ def _get_u_values_and_v_values(self): return u_values, v_values - def _setup_in_uv_space(self): + def _setup_in_uv_space(self) -> None: u_values, v_values = self._get_u_values_and_v_values() faces = VGroup() for i in range(len(u_values) - 1): @@ -197,8 +194,8 @@ def _setup_in_uv_space(self): self.set_fill_by_checkerboard(*self.checkerboard_colors) def set_fill_by_checkerboard( - self, *colors: Sequence[ParsableManimColor], opacity: float = None - ) -> Mobject: + self, *colors: Iterable[ParsableManimColor], opacity: float | None = None + ) -> Self: """Sets the fill_color of each face of :class:`Surface` in an alternating pattern. @@ -227,7 +224,7 @@ def set_fill_by_value( colorscale: list[ParsableManimColor] | ParsableManimColor | None = None, axis: int = 2, **kwargs, - ) -> Mobject: + ) -> Self: """Sets the color of each mobject of a parametric surface to a color relative to its axis-value. @@ -381,9 +378,9 @@ def construct(self): def __init__( self, - center: Sequence[float] = ORIGIN, + center: Point3D = ORIGIN, radius: float = 1, - resolution: Sequence[int] = None, + resolution: Sequence[int] | None = None, u_range: Sequence[float] = (0, TAU), v_range: Sequence[float] = (0, PI), **kwargs, @@ -459,7 +456,7 @@ def __init__( point: list | np.ndarray = ORIGIN, radius: float = DEFAULT_DOT_RADIUS, color: ParsableManimColor = WHITE, - resolution=(8, 8), + resolution: tuple[int, int] = (8, 8), **kwargs, ) -> None: super().__init__(center=point, radius=radius, resolution=resolution, **kwargs) @@ -551,7 +548,9 @@ def construct(self): self.add(prismSmall, prismLarge) """ - def __init__(self, dimensions: Sequence[int] = [3, 2, 1], **kwargs) -> None: + def __init__( + self, dimensions: tuple[float, float, float] | np.ndarray = [3, 2, 1], **kwargs + ) -> None: self.dimensions = dimensions super().__init__(**kwargs) @@ -609,8 +608,8 @@ def __init__( v_range: Sequence[float] = [0, TAU], u_min: float = 0, checkerboard_colors: bool = False, - **kwargs, - ): + **kwargs: Any, + ) -> None: self.direction = direction self.theta = PI - np.arctan(base_radius / height) @@ -662,7 +661,7 @@ def func(self, u: float, v: float) -> np.ndarray: ], ) - def _rotate_to_direction(self): + def _rotate_to_direction(self) -> None: x, y, z = self.direction r = np.sqrt(x**2 + y**2 + z**2) @@ -821,7 +820,7 @@ def add_bases(self) -> None: self.base_bottom.shift(self.u_range[0] * IN) self.add(self.base_top, self.base_bottom) - def _rotate_to_direction(self): + def _rotate_to_direction(self) -> None: x, y, z = self.direction r = np.sqrt(x**2 + y**2 + z**2) @@ -910,7 +909,7 @@ def __init__( start: np.ndarray = LEFT, end: np.ndarray = RIGHT, thickness: float = 0.02, - color: ParsableManimColor = None, + color: ParsableManimColor | None = None, **kwargs, ): self.thickness = thickness @@ -952,7 +951,9 @@ def set_start_and_end_attrs( self.shift((self.start + self.end) / 2) def pointify( - self, mob_or_point: Mobject | float, direction: np.ndarray = None + self, + mob_or_point: Mobject | Point3D, + direction: Vector3 = None, ) -> np.ndarray: """Gets a point representing the center of the :class:`Mobjects <.Mobject>`. @@ -998,7 +999,11 @@ def get_end(self) -> np.ndarray: @classmethod def parallel_to( - cls, line: Line3D, point: Sequence[float] = ORIGIN, length: float = 5, **kwargs + cls, + line: Line3D, + point: Vector3 = ORIGIN, + length: float = 5, + **kwargs, ) -> Line3D: """Returns a line parallel to another line going through a given point. @@ -1042,7 +1047,11 @@ def construct(self): @classmethod def perpendicular_to( - cls, line: Line3D, point: Sequence[float] = ORIGIN, length: float = 5, **kwargs + cls, + line: Line3D, + point: Vector3 = ORIGIN, + length: float = 5, + **kwargs, ) -> Line3D: """Returns a line perpendicular to another line going through a given point. @@ -1191,7 +1200,7 @@ def __init__( minor_radius: float = 1, u_range: Sequence[float] = (0, TAU), v_range: Sequence[float] = (0, TAU), - resolution: Sequence[int] = None, + resolution: tuple[int, int] | None = None, **kwargs, ) -> None: if config.renderer == RendererType.OPENGL: diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index da8e560b59..6048fe4c67 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -14,11 +14,21 @@ import itertools as it import sys -import typing -from typing import Callable, Sequence +from typing import ( + TYPE_CHECKING, + Callable, + Generator, + Hashable, + Iterable, + Literal, + Mapping, + Sequence, +) import numpy as np +import numpy.typing as npt from PIL.Image import Image +from typing_extensions import Self from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject @@ -41,6 +51,21 @@ from ...utils.iterables import make_even, resize_array, stretch_array_to_length, tuplify from ...utils.space_ops import rotate_vector, shoelace_direction +if TYPE_CHECKING: + from manim.typing import ( + BezierPoints, + CubicBezierPoints, + ManimFloat, + MappingFunction, + Point2D, + Point3D, + Point3D_Array, + QuadraticBezierPoints, + RGBA_Array_Float, + Vector3, + Zeros, + ) + # TODO # - Change cubic curve groups to have 4 points instead of 3 # - Change sub_path idea accordingly @@ -80,24 +105,24 @@ class VMobject(Mobject): def __init__( self, fill_color: ParsableManimColor | None = None, - fill_opacity=0.0, + fill_opacity: float = 0.0, stroke_color: ParsableManimColor | None = None, - stroke_opacity=1.0, - stroke_width=DEFAULT_STROKE_WIDTH, + stroke_opacity: float = 1.0, + stroke_width: float = DEFAULT_STROKE_WIDTH, background_stroke_color: ParsableManimColor | None = BLACK, - background_stroke_opacity=1.0, - background_stroke_width=0, - sheen_factor=0.0, + background_stroke_opacity: float = 1.0, + background_stroke_width: float = 0, + sheen_factor: float = 0.0, joint_type: LineJointType | None = None, - sheen_direction=UL, - close_new_points=False, - pre_function_handle_to_anchor_scale_factor=0.01, - make_smooth_after_applying_functions=False, - background_image=None, - shade_in_3d=False, + sheen_direction: Vector3 = UL, + close_new_points: bool = False, + pre_function_handle_to_anchor_scale_factor: float = 0.01, + make_smooth_after_applying_functions: bool = False, + background_image: Image | str | None = None, + shade_in_3d: bool = False, # TODO, do we care about accounting for varying zoom levels? - tolerance_for_point_equality=1e-6, - n_points_per_cubic_curve=4, + tolerance_for_point_equality: float = 1e-6, + n_points_per_cubic_curve: int = 4, **kwargs, ): self.fill_opacity = fill_opacity @@ -107,22 +132,24 @@ def __init__( self.background_stroke_color: ManimColor = ManimColor( background_stroke_color ) - self.background_stroke_opacity = background_stroke_opacity - self.background_stroke_width = background_stroke_width - self.sheen_factor = sheen_factor - if joint_type is None: - joint_type = LineJointType.AUTO - self.joint_type = joint_type - self.sheen_direction = sheen_direction - self.close_new_points = close_new_points - self.pre_function_handle_to_anchor_scale_factor = ( + self.background_stroke_opacity: float = background_stroke_opacity + self.background_stroke_width: float = background_stroke_width + self.sheen_factor: float = sheen_factor + self.joint_type: LineJointType = ( + LineJointType.AUTO if joint_type is None else joint_type + ) + self.sheen_direction: Vector3 = sheen_direction + self.close_new_points: bool = close_new_points + self.pre_function_handle_to_anchor_scale_factor: float = ( pre_function_handle_to_anchor_scale_factor ) - self.make_smooth_after_applying_functions = make_smooth_after_applying_functions - self.background_image = background_image - self.shade_in_3d = shade_in_3d - self.tolerance_for_point_equality = tolerance_for_point_equality - self.n_points_per_cubic_curve = n_points_per_cubic_curve + self.make_smooth_after_applying_functions: bool = ( + make_smooth_after_applying_functions + ) + self.background_image: Image | str | None = background_image + self.shade_in_3d: bool = shade_in_3d + self.tolerance_for_point_equality: float = tolerance_for_point_equality + self.n_points_per_cubic_curve: int = n_points_per_cubic_curve super().__init__(**kwargs) self.submobjects: list[VMobject] @@ -137,18 +164,18 @@ def __init__( # OpenGL compatibility @property - def n_points_per_curve(self): + def n_points_per_curve(self) -> int: return self.n_points_per_cubic_curve - def get_group_class(self): + def get_group_class(self) -> type[VGroup]: return VGroup @staticmethod - def get_mobject_type_class(): + def get_mobject_type_class() -> type[VMobject]: return VMobject # Colors - def init_colors(self, propagate_colors=True): + def init_colors(self, propagate_colors: bool = True) -> Self: self.set_fill( color=self.fill_color, opacity=self.fill_opacity, @@ -178,7 +205,9 @@ def init_colors(self, propagate_colors=True): return self - def generate_rgbas_array(self, color: ManimColor | list[ManimColor], opacity): + def generate_rgbas_array( + self, color: ManimColor | list[ManimColor], opacity: float | Iterable[float] + ) -> RGBA_Array_Float: """ First arg can be either a color, or a tuple/list of colors. Likewise, opacity can either be a float, or a tuple of floats. @@ -189,8 +218,10 @@ def generate_rgbas_array(self, color: ManimColor | list[ManimColor], opacity): colors: list[ManimColor] = [ ManimColor(c) if (c is not None) else BLACK for c in tuplify(color) ] - opacities: list[float] = [o if (o is not None) else 0 for o in tuplify(opacity)] - rgbas = np.array( + opacities: list[float] = [ + o if (o is not None) else 0.0 for o in tuplify(opacity) + ] + rgbas: npt.NDArray[RGBA_Array_Float] = np.array( [c.to_rgba_with_alpha(o) for c, o in zip(*make_even(colors, opacities))], ) @@ -203,8 +234,11 @@ def generate_rgbas_array(self, color: ManimColor | list[ManimColor], opacity): return rgbas def update_rgbas_array( - self, array_name, color: ManimColor | None = None, opacity=None - ): + self, + array_name: str, + color: ManimColor | None = None, + opacity: float | None = None, + ) -> Self: rgbas = self.generate_rgbas_array(color, opacity) if not hasattr(self, array_name): setattr(self, array_name, rgbas) @@ -230,7 +264,7 @@ def set_fill( color: ParsableManimColor | None = None, opacity: float | None = None, family: bool = True, - ): + ) -> Self: """Set the fill color and fill opacity of a :class:`VMobject`. Parameters @@ -270,6 +304,7 @@ def construct(self): for submobject in self.submobjects: submobject.set_fill(color, opacity, family) self.update_rgbas_array("fill_rgbas", color, opacity) + self.fill_rgbas: RGBA_Array_Float if opacity is not None: self.fill_opacity = opacity return self @@ -277,11 +312,11 @@ def construct(self): def set_stroke( self, color: ParsableManimColor = None, - width=None, - opacity=None, + width: float | None = None, + opacity: float | None = None, background=False, - family=True, - ): + family: bool = True, + ) -> Self: if family: for submobject in self.submobjects: submobject.set_stroke(color, width, opacity, background, family) @@ -305,7 +340,7 @@ def set_stroke( self.background_stroke_color = ManimColor(color) return self - def set_background_stroke(self, **kwargs): + def set_background_stroke(self, **kwargs) -> Self: kwargs["background"] = True self.set_stroke(**kwargs) return self @@ -315,16 +350,16 @@ def set_style( fill_color: ParsableManimColor | None = None, fill_opacity: float | None = None, stroke_color: ParsableManimColor | None = None, - stroke_width=None, - stroke_opacity=None, - background_stroke_color: ParsableManimColor = None, - background_stroke_width=None, - background_stroke_opacity=None, - sheen_factor=None, - sheen_direction=None, - background_image=None, - family=True, - ): + stroke_width: float | None = None, + stroke_opacity: float | None = None, + background_stroke_color: ParsableManimColor | None = None, + background_stroke_width: float | None = None, + background_stroke_opacity: float | None = None, + sheen_factor: float | None = None, + sheen_direction: Vector3 | None = None, + background_image: Image | str | None = None, + family: bool = True, + ) -> Self: self.set_fill(color=fill_color, opacity=fill_opacity, family=family) self.set_stroke( color=stroke_color, @@ -348,7 +383,7 @@ def set_style( self.color_using_background_image(background_image) return self - def get_style(self, simple=False): + def get_style(self, simple: bool = False) -> dict: ret = { "stroke_opacity": self.get_stroke_opacity(), "stroke_width": self.get_stroke_width(), @@ -372,7 +407,7 @@ def get_style(self, simple=False): return ret - def match_style(self, vmobject, family=True): + def match_style(self, vmobject: VMobject, family: bool = True) -> Self: self.set_style(**vmobject.get_style(), family=False) if family: @@ -387,18 +422,18 @@ def match_style(self, vmobject, family=True): sm1.match_style(sm2) return self - def set_color(self, color: ParsableManimColor, family=True): + def set_color(self, color: ParsableManimColor, family: bool = True) -> Self: self.set_fill(color, family=family) self.set_stroke(color, family=family) return self - def set_opacity(self, opacity, family=True): + def set_opacity(self, opacity: float, family: bool = True) -> Self: self.set_fill(opacity=opacity, family=family) self.set_stroke(opacity=opacity, family=family) self.set_stroke(opacity=opacity, family=family, background=True) return self - def fade(self, darkness=0.5, family=True): + def fade(self, darkness: float = 0.5, family: bool = True) -> Self: factor = 1.0 - darkness self.set_fill(opacity=factor * self.get_fill_opacity(), family=False) self.set_stroke(opacity=factor * self.get_stroke_opacity(), family=False) @@ -409,13 +444,13 @@ def fade(self, darkness=0.5, family=True): super().fade(darkness, family) return self - def get_fill_rgbas(self): + def get_fill_rgbas(self) -> RGBA_Array_Float | Zeros: try: return self.fill_rgbas except AttributeError: return np.zeros((1, 4)) - def get_fill_color(self): + def get_fill_color(self) -> ManimColor: """ If there are multiple colors (for gradient) this returns the first one @@ -424,7 +459,7 @@ def get_fill_color(self): fill_color = property(get_fill_color, set_fill) - def get_fill_opacity(self): + def get_fill_opacity(self) -> ManimFloat: """ If there are multiple opacities, this returns the first @@ -432,59 +467,63 @@ def get_fill_opacity(self): return self.get_fill_opacities()[0] # TODO: Does this just do a copy? - def get_fill_colors(self): + # TODO: I have the feeling that this function should not return None, does that have any usage ? + def get_fill_colors(self) -> list[ManimColor | None]: return [ ManimColor(rgba[:3]) if rgba.any() else None for rgba in self.get_fill_rgbas() ] - def get_fill_opacities(self): + def get_fill_opacities(self) -> npt.NDArray[ManimFloat]: return self.get_fill_rgbas()[:, 3] - def get_stroke_rgbas(self, background=False): + def get_stroke_rgbas(self, background: bool = False) -> RGBA_Array_float | Zeros: try: if background: + self.background_stroke_rgbas: RGBA_Array_Float rgbas = self.background_stroke_rgbas else: + self.stroke_rgbas: RGBA_Array_Float rgbas = self.stroke_rgbas return rgbas except AttributeError: return np.zeros((1, 4)) - def get_stroke_color(self, background=False): + def get_stroke_color(self, background: bool = False) -> ManimColor | None: return self.get_stroke_colors(background)[0] stroke_color = property(get_stroke_color, set_stroke) - def get_stroke_width(self, background=False): + def get_stroke_width(self, background: bool = False) -> float: if background: + self.background_stroke_width: float width = self.background_stroke_width else: width = self.stroke_width if isinstance(width, str): width = int(width) - return max(0, width) + return max(0.0, width) - def get_stroke_opacity(self, background=False): + def get_stroke_opacity(self, background: bool = False) -> ManimFloat: return self.get_stroke_opacities(background)[0] - def get_stroke_colors(self, background=False): + def get_stroke_colors(self, background: bool = False) -> list[ManimColor | None]: return [ ManimColor(rgba[:3]) if rgba.any() else None for rgba in self.get_stroke_rgbas(background) ] - def get_stroke_opacities(self, background=False): + def get_stroke_opacities(self, background: bool = False) -> npt.NDArray[ManimFloat]: return self.get_stroke_rgbas(background)[:, 3] - def get_color(self): + def get_color(self) -> ManimColor: if np.all(self.get_fill_opacities() == 0): return self.get_stroke_color() return self.get_fill_color() color = property(get_color, set_color) - def set_sheen_direction(self, direction: np.ndarray, family=True): + def set_sheen_direction(self, direction: Vector3, family: bool = True) -> Self: """Sets the direction of the applied sheen. Parameters @@ -509,10 +548,12 @@ def set_sheen_direction(self, direction: np.ndarray, family=True): for submob in self.get_family(): submob.sheen_direction = direction else: - self.sheen_direction = direction + self.sheen_direction: Vector3 = direction return self - def rotate_sheen_direction(self, angle: float, axis: np.ndarray = OUT, family=True): + def rotate_sheen_direction( + self, angle: float, axis: Vector3 = OUT, family: bool = True + ) -> Self: """Rotates the direction of the applied sheen. Parameters @@ -543,7 +584,9 @@ def rotate_sheen_direction(self, angle: float, axis: np.ndarray = OUT, family=Tr self.sheen_direction = rotate_vector(self.sheen_direction, angle, axis) return self - def set_sheen(self, factor: float, direction: np.ndarray = None, family=True): + def set_sheen( + self, factor: float, direction: Vector3 | None = None, family: bool = True + ) -> Self: """Applies a color gradient from a direction. Parameters @@ -569,7 +612,7 @@ def construct(self): if family: for submob in self.submobjects: submob.set_sheen(factor, direction, family) - self.sheen_factor = factor + self.sheen_factor: float = factor if direction is not None: # family set to false because recursion will # already be handled above @@ -580,13 +623,13 @@ def construct(self): self.set_fill(self.get_fill_color(), family=family) return self - def get_sheen_direction(self): + def get_sheen_direction(self) -> Vector3: return np.array(self.sheen_direction) - def get_sheen_factor(self): + def get_sheen_factor(self) -> float: return self.sheen_factor - def get_gradient_start_and_end_points(self): + def get_gradient_start_and_end_points(self) -> tuple[Point3D, Point3D]: if self.shade_in_3d: return get_3d_vmob_gradient_start_and_end_points(self) else: @@ -598,8 +641,8 @@ def get_gradient_start_and_end_points(self): offset = np.dot(bases, direction) return (c - offset, c + offset) - def color_using_background_image(self, background_image: Image | str): - self.background_image = background_image + def color_using_background_image(self, background_image: Image | str) -> Self: + self.background_image: Image | str = background_image self.set_color(WHITE) for submob in self.submobjects: submob.color_using_background_image(background_image) @@ -608,26 +651,28 @@ def color_using_background_image(self, background_image: Image | str): def get_background_image(self) -> Image | str: return self.background_image - def match_background_image(self, vmobject): + def match_background_image(self, vmobject: VMobject) -> Self: self.color_using_background_image(vmobject.get_background_image()) return self - def set_shade_in_3d(self, value=True, z_index_as_group=False): + def set_shade_in_3d( + self, value: bool = True, z_index_as_group: bool = False + ) -> Self: for submob in self.get_family(): submob.shade_in_3d = value if z_index_as_group: submob.z_index_group = self return self - def set_points(self, points): - self.points = np.array(points) + def set_points(self, points: Point3D_Array) -> Self: + self.points: Point3D_Array = np.array(points) return self def resize_points( self, new_length: int, - resize_func: Callable[[np.ndarray, int], np.ndarray] = resize_array, - ): + resize_func: Callable[[Point3D, int], Point3D] = resize_array, + ) -> Self: """Resize the array of anchor points and handles to have the specified size. @@ -646,11 +691,11 @@ def resize_points( def set_anchors_and_handles( self, - anchors1: Sequence[float], - handles1: Sequence[float], - handles2: Sequence[float], - anchors2: Sequence[float], - ): + anchors1: CubicBezierPoints, + handles1: CubicBezierPoints, + handles2: CubicBezierPoints, + anchors2: CubicBezierPoints, + ) -> Self: """Given two sets of anchors and handles, process them to set them as anchors and handles of the VMobject. @@ -678,17 +723,17 @@ def set_anchors_and_handles( self.points[index::nppcc] = array return self - def clear_points(self): + def clear_points(self) -> None: self.points = np.zeros((0, self.dim)) - def append_points(self, new_points): + def append_points(self, new_points: Point3D_Array) -> Self: # TODO, check that number new points is a multiple of 4? # or else that if len(self.points) % 4 == 1, then # len(new_points) % 4 == 3? self.points = np.append(self.points, new_points, axis=0) return self - def start_new_path(self, point): + def start_new_path(self, point: Point3D) -> Self: if len(self.points) % 4 != 0: # close the open path by appending the last # start anchor sufficiently often @@ -700,23 +745,24 @@ def start_new_path(self, point): def add_cubic_bezier_curve( self, - anchor1: np.ndarray, - handle1: np.ndarray, - handle2: np.ndarray, - anchor2, + anchor1: CubicBezierPoints, + handle1: CubicBezierPoints, + handle2: CubicBezierPoints, + anchor2: CubicBezierPoints, ) -> None: # TODO, check the len(self.points) % 4 == 0? self.append_points([anchor1, handle1, handle2, anchor2]) - def add_cubic_bezier_curves(self, curves): + # what type is curves? + def add_cubic_bezier_curves(self, curves) -> None: self.append_points(curves.flatten()) def add_cubic_bezier_curve_to( self, - handle1: np.ndarray, - handle2: np.ndarray, - anchor: np.ndarray, - ): + handle1: CubicBezierPoints, + handle2: CubicBezierPoints, + anchor: CubicBezierPoints, + ) -> Self: """Add cubic bezier curve to the path. NOTE : the first anchor is not a parameter as by default the end of the last sub-path! @@ -745,9 +791,9 @@ def add_cubic_bezier_curve_to( def add_quadratic_bezier_curve_to( self, - handle: np.ndarray, - anchor: np.ndarray, - ): + handle: QuadraticBezierPoints, + anchor: QuadraticBezierPoints, + ) -> Self: """Add Quadratic bezier curve to the path. Returns @@ -769,7 +815,7 @@ def add_quadratic_bezier_curve_to( ) return self - def add_line_to(self, point: np.ndarray): + def add_line_to(self, point: Point3D) -> Self: """Add a straight line from the last point of VMobject to the given point. Parameters @@ -792,7 +838,7 @@ def add_line_to(self, point: np.ndarray): ) return self - def add_smooth_curve_to(self, *points: np.array): + def add_smooth_curve_to(self, *points: Point3D) -> Self: """Creates a smooth curve from given points and add it to the VMobject. If two points are passed in, the first is interpreted as a handle, the second as an anchor. @@ -835,28 +881,28 @@ def add_smooth_curve_to(self, *points: np.array): self.append_points([last_a2, handle1, handle2, new_anchor]) return self - def has_new_path_started(self): + def has_new_path_started(self) -> bool: nppcc = self.n_points_per_cubic_curve # 4 # A new path starting is defined by a control point which is not part of a bezier subcurve. return len(self.points) % nppcc == 1 - def get_last_point(self): + def get_last_point(self) -> Point3D: return self.points[-1] - def is_closed(self): + def is_closed(self) -> bool: # TODO use consider_points_equals_2d ? return self.consider_points_equals(self.points[0], self.points[-1]) - def close_path(self): + def close_path(self) -> None: if not self.is_closed(): self.add_line_to(self.get_subpaths()[-1][0]) - def add_points_as_corners(self, points: np.ndarray) -> VMobject: + def add_points_as_corners(self, points: Iterable[Point3D]) -> Iterable[Point3D]: for point in points: self.add_line_to(point) return points - def set_points_as_corners(self, points: Sequence[float]): + def set_points_as_corners(self, points: Point3D_Array) -> Self: """Given an array of points, set them as corner of the vmobject. To achieve that, this algorithm sets handles aligned with the anchors such that the resultant bezier curve will be the segment @@ -881,12 +927,12 @@ def set_points_as_corners(self, points: Sequence[float]): ) return self - def set_points_smoothly(self, points): + def set_points_smoothly(self, points: Point3D_Array) -> Self: self.set_points_as_corners(points) self.make_smooth() return self - def change_anchor_mode(self, mode: str): + def change_anchor_mode(self, mode: Literal["jagged", "smooth"]) -> Self: """Changes the anchor mode of the bezier curves. This will modify the handles. There can be only two modes, "jagged", and "smooth". @@ -896,7 +942,7 @@ def change_anchor_mode(self, mode: str): :class:`VMobject` ``self`` """ - assert mode in ["jagged", "smooth"] + assert mode in ["jagged", "smooth"], 'mode must be either "jagged" or "smooth"' nppcc = self.n_points_per_cubic_curve for submob in self.family_members_with_points(): subpaths = submob.get_subpaths() @@ -908,7 +954,7 @@ def change_anchor_mode(self, mode: str): anchors = np.append(subpath[::nppcc], subpath[-1:], 0) if mode == "smooth": h1, h2 = get_smooth_handle_points(anchors) - elif mode == "jagged": + else: # mode == "jagged" # The following will make the handles aligned with the anchors, thus making the bezier curve a segment a1 = anchors[:-1] a2 = anchors[1:] @@ -920,18 +966,18 @@ def change_anchor_mode(self, mode: str): submob.append_points(new_subpath) return self - def make_smooth(self): + def make_smooth(self) -> Self: return self.change_anchor_mode("smooth") - def make_jagged(self): + def make_jagged(self) -> Self: return self.change_anchor_mode("jagged") - def add_subpath(self, points: np.ndarray): + def add_subpath(self, points: Point3D_Array) -> Self: assert len(points) % 4 == 0 - self.points = np.append(self.points, points, axis=0) + self.points: Point3D_Array = np.append(self.points, points, axis=0) return self - def append_vectorized_mobject(self, vectorized_mobject): + def append_vectorized_mobject(self, vectorized_mobject: VMobject) -> None: new_points = list(vectorized_mobject.points) if self.has_new_path_started(): @@ -940,7 +986,7 @@ def append_vectorized_mobject(self, vectorized_mobject): self.points = self.points[:-1] self.append_points(new_points) - def apply_function(self, function): + def apply_function(self, function: MappingFunction) -> Self: factor = self.pre_function_handle_to_anchor_scale_factor self.scale_handle_to_anchor_distances(factor) super().apply_function(function) @@ -952,15 +998,15 @@ def apply_function(self, function): def rotate( self, angle: float, - axis: np.ndarray = OUT, - about_point: Sequence[float] | None = None, + axis: Vector3 = OUT, + about_point: Point3D | None = None, **kwargs, - ): + ) -> Self: self.rotate_sheen_direction(angle, axis) super().rotate(angle, axis, about_point, **kwargs) return self - def scale_handle_to_anchor_distances(self, factor: float): + def scale_handle_to_anchor_distances(self, factor: float) -> Self: """If the distance between a given handle point H and its associated anchor point A is d, then it changes H to be a distances factor*d away from A, but so that the line from A to H doesn't change. @@ -992,10 +1038,10 @@ def scale_handle_to_anchor_distances(self, factor: float): return self # - def consider_points_equals(self, p0, p1): + def consider_points_equals(self, p0: Point3D, p1: Point3D) -> bool: return np.allclose(p0, p1, atol=self.tolerance_for_point_equality) - def consider_points_equals_2d(self, p0: np.ndarray, p1: np.ndarray) -> bool: + def consider_points_equals_2d(self, p0: Point2D, p1: Point2D) -> bool: """Determine if two points are close enough to be considered equal. This uses the algorithm from np.isclose(), but expanded here for the @@ -1021,10 +1067,14 @@ def consider_points_equals_2d(self, p0: np.ndarray, p1: np.ndarray) -> bool: return True # Information about line - def get_cubic_bezier_tuples_from_points(self, points): - return np.array(list(self.gen_cubic_bezier_tuples_from_points(points))) - - def gen_cubic_bezier_tuples_from_points(self, points: np.ndarray) -> tuple: + def get_cubic_bezier_tuples_from_points( + self, points: Point3D_Array + ) -> npt.NDArray[Point3D_Array]: + return np.array(self.gen_cubic_bezier_tuples_from_points(points)) + + def gen_cubic_bezier_tuples_from_points( + self, points: Point3D_Array + ) -> tuple[Point3D_Array]: """Returns the bezier tuples from an array of points. self.points is a list of the anchors and handles of the bezier curves of the mobject (ie [anchor1, handle1, handle2, anchor2, anchor3 ..]) @@ -1039,23 +1089,23 @@ def gen_cubic_bezier_tuples_from_points(self, points: np.ndarray) -> tuple: Returns ------- - typing.Tuple + tuple Bezier control points. """ nppcc = self.n_points_per_cubic_curve remainder = len(points) % nppcc points = points[: len(points) - remainder] # Basically take every nppcc element. - return (points[i : i + nppcc] for i in range(0, len(points), nppcc)) + return tuple(points[i : i + nppcc] for i in range(0, len(points), nppcc)) - def get_cubic_bezier_tuples(self): + def get_cubic_bezier_tuples(self) -> npt.NDArray[Point3D_Array]: return self.get_cubic_bezier_tuples_from_points(self.points) def _gen_subpaths_from_points( self, - points: np.ndarray, - filter_func: typing.Callable[[int], bool], - ) -> tuple: + points: Point3D_Array, + filter_func: Callable[[int], bool], + ) -> Generator[Point3D_Array]: """Given an array of points defining the bezier curves of the vmobject, return subpaths formed by these points. Here, Two bezier curves form a path if at least two of their anchors are evaluated True by the relation defined by filter_func. @@ -1073,7 +1123,7 @@ def _gen_subpaths_from_points( Returns ------- - typing.Tuple + Generator[Point3D_Array] subpaths formed by the points. """ nppcc = self.n_points_per_cubic_curve @@ -1085,7 +1135,7 @@ def _gen_subpaths_from_points( if (i2 - i1) >= nppcc ) - def get_subpaths_from_points(self, points): + def get_subpaths_from_points(self, points: Point3D_Array) -> list[Point3D_Array]: return list( self._gen_subpaths_from_points( points, @@ -1093,25 +1143,27 @@ def get_subpaths_from_points(self, points): ), ) - def gen_subpaths_from_points_2d(self, points): + def gen_subpaths_from_points_2d( + self, points: Point3D_Array + ) -> Generator[Point3D_Array]: return self._gen_subpaths_from_points( points, lambda n: not self.consider_points_equals_2d(points[n - 1], points[n]), ) - def get_subpaths(self) -> tuple: + def get_subpaths(self) -> list[Point3D_Array]: """Returns subpaths formed by the curves of the VMobject. Subpaths are ranges of curves with each pair of consecutive curves having their end/start points coincident. Returns ------- - typing.Tuple + list[Point3D_Array] subpaths. """ return self.get_subpaths_from_points(self.points) - def get_nth_curve_points(self, n: int) -> np.ndarray: + def get_nth_curve_points(self, n: int) -> Point3D_Array: """Returns the points defining the nth curve of the vmobject. Parameters @@ -1121,14 +1173,14 @@ def get_nth_curve_points(self, n: int) -> np.ndarray: Returns ------- - np.ndarray - points defininf the nth bezier curve (anchors, handles) + Point3D_Array + points defining the nth bezier curve (anchors, handles) """ assert n < self.get_num_curves() nppcc = self.n_points_per_cubic_curve return self.points[nppcc * n : nppcc * (n + 1)] - def get_nth_curve_function(self, n: int) -> typing.Callable[[float], np.ndarray]: + def get_nth_curve_function(self, n: int) -> Callable[[float], Point3D]: """Returns the expression of the nth curve. Parameters @@ -1138,7 +1190,7 @@ def get_nth_curve_function(self, n: int) -> typing.Callable[[float], np.ndarray] Returns ------- - typing.Callable[float] + Callable[float, Point3D] expression of the nth bezier curve. """ return bezier(self.get_nth_curve_points(n)) @@ -1147,7 +1199,7 @@ def get_nth_curve_length_pieces( self, n: int, sample_points: int | None = None, - ) -> np.ndarray: + ) -> npt.NDArray[ManimFloat]: """Returns the array of short line lengths used for length approximation. Parameters @@ -1159,7 +1211,6 @@ def get_nth_curve_length_pieces( Returns ------- - np.ndarray The short length-pieces of the nth curve. """ if sample_points is None: @@ -1200,7 +1251,7 @@ def get_nth_curve_function_with_length( self, n: int, sample_points: int | None = None, - ) -> tuple[typing.Callable[[float], np.ndarray], float]: + ) -> tuple[Callable[[float], Point3D], float]: """Returns the expression of the nth curve along with its (approximate) length. Parameters @@ -1212,7 +1263,7 @@ def get_nth_curve_function_with_length( Returns ------- - curve : typing.Callable[[float], np.ndarray] + curve : Callable[[float], Point3D] The function for the nth curve. length : :class:`float` The length of the nth curve. @@ -1230,19 +1281,19 @@ def get_num_curves(self) -> int: Returns ------- int - number of curves. of the vmobject. + number of curves of the vmobject. """ nppcc = self.n_points_per_cubic_curve return len(self.points) // nppcc def get_curve_functions( self, - ) -> typing.Iterable[typing.Callable[[float], np.ndarray]]: + ) -> Generator[Callable[[float], Point3D]]: """Gets the functions for the curves of the mobject. Returns ------- - typing.Iterable[typing.Callable[[float], np.ndarray]] + Generator[Callable[[float], Point3D]] The functions for the curves. """ @@ -1253,7 +1304,7 @@ def get_curve_functions( def get_curve_functions_with_lengths( self, **kwargs - ) -> typing.Iterable[tuple[typing.Callable[[float], np.ndarray], float]]: + ) -> Generator[tuple[Callable[[float], Point3D], float]]: """Gets the functions and lengths of the curves for the mobject. Parameters @@ -1263,7 +1314,7 @@ def get_curve_functions_with_lengths( Returns ------- - typing.Iterable[typing.Tuple[typing.Callable[[float], np.ndarray], float]] + Generator[tuple[Callable[[float], Point3D], float]] The functions and lengths of the curves. """ @@ -1272,7 +1323,7 @@ def get_curve_functions_with_lengths( for n in range(num_curves): yield self.get_nth_curve_function_with_length(n, **kwargs) - def point_from_proportion(self, alpha: float) -> np.ndarray: + def point_from_proportion(self, alpha: float) -> Point3D: """Gets the point at a proportion along the path of the :class:`VMobject`. Parameters @@ -1318,7 +1369,7 @@ def point_from_proportion(self, alpha: float) -> np.ndarray: def proportion_from_point( self, - point: typing.Iterable[float | int], + point: Iterable[float | int], ) -> float: """Returns the proportion along the path of the :class:`VMobject` a particular given point is at. @@ -1372,7 +1423,7 @@ def proportion_from_point( return alpha - def get_anchors_and_handles(self) -> typing.Iterable[np.ndarray]: + def get_anchors_and_handles(self) -> list[Point3D_Array]: """Returns anchors1, handles1, handles2, anchors2, where (anchors1[i], handles1[i], handles2[i], anchors2[i]) will be four points defining a cubic bezier curve @@ -1380,50 +1431,52 @@ def get_anchors_and_handles(self) -> typing.Iterable[np.ndarray]: Returns ------- - typing.Iterable[np.ndarray] + `list[Point3D_Array]` Iterable of the anchors and handles. """ nppcc = self.n_points_per_cubic_curve return [self.points[i::nppcc] for i in range(nppcc)] - def get_start_anchors(self) -> np.ndarray: + def get_start_anchors(self) -> Point3D_Array: """Returns the start anchors of the bezier curves. Returns ------- - np.ndarray + Point3D_Array Starting anchors """ - return self.points[0 :: self.n_points_per_cubic_curve] + return self.points[:: self.n_points_per_cubic_curve] - def get_end_anchors(self) -> np.ndarray: + def get_end_anchors(self) -> Point3D_Array: """Return the end anchors of the bezier curves. Returns ------- - np.ndarray + Point3D_Array Starting anchors """ nppcc = self.n_points_per_cubic_curve return self.points[nppcc - 1 :: nppcc] - def get_anchors(self) -> np.ndarray: + def get_anchors(self) -> Point3D_Array: """Returns the anchors of the curves forming the VMobject. Returns ------- - np.ndarray + Point3D_Array The anchors. """ if self.points.shape[0] == 1: return self.points return np.array( - list(it.chain(*zip(self.get_start_anchors(), self.get_end_anchors()))), + tuple(it.chain(*zip(self.get_start_anchors(), self.get_end_anchors()))), ) - def get_points_defining_boundary(self): + def get_points_defining_boundary(self) -> Point3D_Array: # Probably returns all anchors, but this is weird regarding the name of the method. - return np.array(list(it.chain(*(sm.get_anchors() for sm in self.get_family())))) + return np.array( + tuple(it.chain(*(sm.get_anchors() for sm in self.get_family()))) + ) def get_arc_length(self, sample_points_per_curve: int | None = None) -> float: """Return the approximated length of the whole curve. @@ -1447,7 +1500,7 @@ def get_arc_length(self, sample_points_per_curve: int | None = None) -> float: ) # Alignment - def align_points(self, vmobject: VMobject): + def align_points(self, vmobject: VMobject) -> Self: """Adds points to self and vmobject so that they both have the same number of subpaths, with corresponding subpaths each containing the same number of points. @@ -1518,7 +1571,7 @@ def get_nth_subpath(path_list, n): vmobject.set_points(new_path2) return self - def insert_n_curves(self, n: int): + def insert_n_curves(self, n: int) -> Self: """Inserts n curves to the bezier curves of the vmobject. Parameters @@ -1542,7 +1595,9 @@ def insert_n_curves(self, n: int): self.append_points([new_path_point]) return self - def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarray: + def insert_n_curves_to_point_list( + self, n: int, points: Point3D_Array + ) -> npt.NDArray[BezierPoints]: """Given an array of k points defining a bezier curves (anchors and handles), returns points defining exactly k + n bezier curves. Parameters @@ -1554,7 +1609,6 @@ def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarra Returns ------- - np.ndarray Points generated. """ @@ -1597,7 +1651,7 @@ def insert_n_curves_to_point_list(self, n: int, points: np.ndarray) -> np.ndarra ) return new_points - def align_rgbas(self, vmobject): + def align_rgbas(self, vmobject: VMobject) -> Self: attrs = ["fill_rgbas", "stroke_rgbas", "background_stroke_rgbas"] for attr in attrs: a1 = getattr(self, attr) @@ -1610,14 +1664,16 @@ def align_rgbas(self, vmobject): setattr(self, attr, new_a1) return self - def get_point_mobject(self, center=None): + def get_point_mobject(self, center: Point3D | None = None) -> VectorizedPoint: if center is None: center = self.get_center() point = VectorizedPoint(center) point.match_style(self) return point - def interpolate_color(self, mobject1, mobject2, alpha): + def interpolate_color( + self, mobject1: VMobject, mobject2: VMobject, alpha: float + ) -> None: attrs = [ "fill_rgbas", "stroke_rgbas", @@ -1641,7 +1697,7 @@ def pointwise_become_partial( vmobject: VMobject, a: float, b: float, - ): + ) -> Self: """Given two bounds a and b, transforms the points of the self vmobject into the points of the vmobject passed as parameter with respect to the bounds. Points here stand for control points of the bezier curves (anchors and handles) @@ -1698,7 +1754,7 @@ def pointwise_become_partial( ) return self - def get_subcurve(self, a: float, b: float) -> VMobject: + def get_subcurve(self, a: float, b: float) -> Self: """Returns the subcurve of the VMobject between the interval [a, b]. The curve is a VMobject itself. @@ -1726,7 +1782,7 @@ def get_subcurve(self, a: float, b: float) -> VMobject: vmob.pointwise_become_partial(self, a, b) return vmob - def get_direction(self): + def get_direction(self) -> Literal["CW", "CCW"]: """Uses :func:`~.space_ops.shoelace_direction` to calculate the direction. The direction of points determines in which direction the object is drawn, clockwise or counterclockwise. @@ -1746,7 +1802,7 @@ def get_direction(self): """ return shoelace_direction(self.get_start_anchors()) - def reverse_direction(self): + def reverse_direction(self) -> Self: """Reverts the point direction by inverting the point order. Returns @@ -1771,7 +1827,7 @@ def construct(self): self.points = self.points[::-1] return self - def force_direction(self, target_direction: str): + def force_direction(self, target_direction: Literal["CW", "CCW"]) -> Self: """Makes sure that points are either directed clockwise or counterclockwise. @@ -1850,21 +1906,16 @@ def __init__(self, *vmobjects, **kwargs): super().__init__(**kwargs) self.add(*vmobjects) - def __repr__(self): - return ( - self.__class__.__name__ - + "(" - + ", ".join(str(mob) for mob in self.submobjects) - + ")" - ) + def __repr__(self) -> str: + return f'{self.__class__.__name__}({", ".join(str(mob) for mob in self.submobjects)})' - def __str__(self): + def __str__(self) -> str: return ( f"{self.__class__.__name__} of {len(self.submobjects)} " f"submobject{'s' if len(self.submobjects) > 0 else ''}" ) - def add(self, *vmobjects: VMobject): + def add(self, *vmobjects: VMobject) -> Self: """Checks if all passed elements are an instance of VMobject and then add them to submobjects Parameters @@ -1916,21 +1967,21 @@ def construct(self): raise TypeError("All submobjects must be of type VMobject") return super().add(*vmobjects) - def __add__(self, vmobject): + def __add__(self, vmobject: VMobject) -> Self: return VGroup(*self.submobjects, vmobject) - def __iadd__(self, vmobject): + def __iadd__(self, vmobject: VMobject) -> Self: return self.add(vmobject) - def __sub__(self, vmobject): + def __sub__(self, vmobject: VMobject) -> Self: copy = VGroup(*self.submobjects) copy.remove(vmobject) return copy - def __isub__(self, vmobject): + def __isub__(self, vmobject: VMobject) -> Self: return self.remove(vmobject) - def __setitem__(self, key: int, value: VMobject | typing.Sequence[VMobject]): + def __setitem__(self, key: int, value: VMobject | Sequence[VMobject]) -> None: """Override the [] operator for item assignment. Parameters @@ -2057,27 +2108,25 @@ def construct(self): def __init__( self, mapping_or_iterable: ( - typing.Mapping[typing.Hashable, VMobject] - | typing.Iterable[tuple[typing.Hashable, VMobject]] + Mapping[Hashable, VMobject] | Iterable[tuple[Hashable, VMobject]] ) = {}, show_keys: bool = False, **kwargs, - ): + ) -> None: super().__init__(**kwargs) self.show_keys = show_keys self.submob_dict = {} self.add(mapping_or_iterable) - def __repr__(self): - return __class__.__name__ + "(" + repr(self.submob_dict) + ")" + def __repr__(self) -> str: + return f"{self.__class__.__name__}({repr(self.submob_dict)})" def add( self, mapping_or_iterable: ( - typing.Mapping[typing.Hashable, VMobject] - | typing.Iterable[tuple[typing.Hashable, VMobject]] + Mapping[Hashable, VMobject] | Iterable[tuple[Hashable, VMobject]] ), - ): + ) -> Self: """Adds the key-value pairs to the :class:`VDict` object. Also, it internally adds the value to the `submobjects` :class:`list` @@ -2105,7 +2154,7 @@ def add( return self - def remove(self, key: typing.Hashable): + def remove(self, key: Hashable) -> Self: """Removes the mobject from the :class:`VDict` object having the key `key` Also, it internally removes the mobject from the `submobjects` :class:`list` @@ -2133,7 +2182,7 @@ def remove(self, key: typing.Hashable): del self.submob_dict[key] return self - def __getitem__(self, key: typing.Hashable): + def __getitem__(self, key: Hashable): """Override the [] operator for item retrieval. Parameters @@ -2155,7 +2204,7 @@ def __getitem__(self, key: typing.Hashable): submob = self.submob_dict[key] return submob - def __setitem__(self, key: typing.Hashable, value: VMobject): + def __setitem__(self, key: Hashable, value: VMobject) -> None: """Override the [] operator for item assignment. Parameters @@ -2180,7 +2229,7 @@ def __setitem__(self, key: typing.Hashable, value: VMobject): self.remove(key) self.add([(key, value)]) - def __delitem__(self, key: typing.Hashable): + def __delitem__(self, key: Hashable): """Override the del operator for deleting an item. Parameters @@ -2212,7 +2261,7 @@ def __delitem__(self, key: typing.Hashable): """ del self.submob_dict[key] - def __contains__(self, key: typing.Hashable): + def __contains__(self, key: Hashable): """Override the in operator. Parameters @@ -2236,7 +2285,7 @@ def __contains__(self, key: typing.Hashable): """ return key in self.submob_dict - def get_all_submobjects(self): + def get_all_submobjects(self) -> list[list]: """To get all the submobjects associated with a particular :class:`VDict` object Returns @@ -2254,7 +2303,7 @@ def get_all_submobjects(self): submobjects = self.submob_dict.values() return submobjects - def add_key_value_pair(self, key: typing.Hashable, value: VMobject): + def add_key_value_pair(self, key: Hashable, value: VMobject) -> None: """A utility function used by :meth:`add` to add the key-value pair to :attr:`submob_dict`. Not really meant to be used externally. @@ -2299,14 +2348,14 @@ def add_key_value_pair(self, key: typing.Hashable, value: VMobject): class VectorizedPoint(VMobject, metaclass=ConvertToOpenGL): def __init__( self, - location=ORIGIN, - color=BLACK, - fill_opacity=0, - stroke_width=0, - artificial_width=0.01, - artificial_height=0.01, + location: Point3D = ORIGIN, + color: ManimColor = BLACK, + fill_opacity: float = 0, + stroke_width: float = 0, + artificial_width: float = 0.01, + artificial_height: float = 0.01, **kwargs, - ): + ) -> None: self.artificial_width = artificial_width self.artificial_height = artificial_height super().__init__( @@ -2320,17 +2369,17 @@ def __init__( basecls = OpenGLVMobject if config.renderer == RendererType.OPENGL else VMobject @basecls.width.getter - def width(self): + def width(self) -> float: return self.artificial_width @basecls.height.getter - def height(self): + def height(self) -> float: return self.artificial_height - def get_location(self): + def get_location(self) -> Point3D: return np.array(self.points[0]) - def set_location(self, new_loc): + def set_location(self, new_loc: Point3D): self.set_points(np.array([new_loc])) @@ -2351,7 +2400,7 @@ def construct(self): """ - def __init__(self, vmobject, **kwargs): + def __init__(self, vmobject: VMobject, **kwargs) -> None: super().__init__(**kwargs) tuples = vmobject.get_cubic_bezier_tuples() for tup in tuples: @@ -2360,7 +2409,7 @@ def __init__(self, vmobject, **kwargs): part.match_style(vmobject) self.add(part) - def point_from_proportion(self, alpha: float) -> np.ndarray: + def point_from_proportion(self, alpha: float) -> Point3D: """Gets the point at a proportion along the path of the :class:`CurvesAsSubmobjects`. Parameters @@ -2480,14 +2529,14 @@ def construct(self): def __init__( self, - vmobject, - num_dashes=15, - dashed_ratio=0.5, - dash_offset=0, - color=WHITE, - equal_lengths=True, + vmobject: VMobject, + num_dashes: int = 15, + dashed_ratio: float = 0.5, + dash_offset: float = 0, + color: ManimColor = WHITE, + equal_lengths: bool = True, **kwargs, - ): + ) -> None: self.dashed_ratio = dashed_ratio self.num_dashes = num_dashes super().__init__(color=color, **kwargs) diff --git a/manim/typing.py b/manim/typing.py new file mode 100644 index 0000000000..495f199758 --- /dev/null +++ b/manim/typing.py @@ -0,0 +1,133 @@ +from __future__ import annotations + +from os import PathLike +from typing import Callable, Tuple, Union + +import numpy as np +import numpy.typing as npt +from typing_extensions import TypeAlias + +# Color Types + +ManimFloat: TypeAlias = np.float64 +ManimInt: TypeAlias = np.int64 +ManimColorDType: TypeAlias = ManimFloat + +RGB_Array_Float: TypeAlias = npt.NDArray[ManimFloat] +RGB_Tuple_Float: TypeAlias = Tuple[float, float, float] + +RGB_Array_Int: TypeAlias = npt.NDArray[ManimInt] +RGB_Tuple_Int: TypeAlias = Tuple[int, int, int] + +RGBA_Array_Float: TypeAlias = npt.NDArray[ManimFloat] +RGBA_Tuple_Float: TypeAlias = Tuple[float, float, float, float] + +RGBA_Array_Int: TypeAlias = npt.NDArray[ManimInt] +RGBA_Tuple_Int: TypeAlias = Tuple[int, int, int, int] + +HSV_Array_Float: TypeAlias = RGB_Array_Float +HSV_Tuple_Float: TypeAlias = RGB_Tuple_Float + +ManimColorInternal: TypeAlias = npt.NDArray[ManimColorDType] + +# Point Types + +PointDType: TypeAlias = ManimFloat +""" DType for all points. """ + +InternalPoint2D: TypeAlias = npt.NDArray[PointDType] +""" `shape: (2,)` A 2D point. `[float, float]`. +This type alias is mostly made available for internal use and only includes the numpy type. +""" + +Point2D: TypeAlias = Union[InternalPoint2D, Tuple[float, float]] +""" `shape: (2,)` A 2D point. `[float, float]`. """ + +InternalPoint3D: TypeAlias = npt.NDArray[PointDType] +""" `shape: (3,)` A 3D point. `[float, float, float]`. +This type alias is mostly made available for internal use and only includes the numpy type. +""" + +Point3D: TypeAlias = Union[InternalPoint3D, Tuple[float, float, float]] +""" `shape: (3,)` A 3D point. `[float, float, float]` """ + +# Bezier Types +QuadraticBezierPoints: TypeAlias = npt.NDArray[PointDType] +""" `shape: (3,3)` An Array of Quadratic Bezier Handles `[[float, float, float], [float, float, float], [float, float, float]]`. """ + +QuadraticBezierPoints_Array: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,3,3)` An Array of Quadratic Bezier Handles `[[[float, float, float], [float, float, float], [float, float, float]], ...]`. """ + +CubicBezierPoints: TypeAlias = npt.NDArray[PointDType] +""" `shape: (4,3)` An Array of Cubic Bezier Handles `[[float, float, float], [float, float, float], [float, float, float], [float, float, float]]`. """ + +BezierPoints: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,3)` An Array of Cubic Bezier Handles `[[float, float, float], ...]`. +`N` Is always multiples of the degree of the Bezier curve. +(Please refer to the documentation of the function you are using for further type Information) +""" + +FlatBezierPoints: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N)` An Array of Bezier Handles but flattened `[float, ...]`.""" + +Point2D_Array: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,2)` An Array of Points in 2D Space `[[float, float], ...]`. + +(Please refer to the documentation of the function you are using for further type Information) +""" + +InternalPoint3D_Array: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,3)` An Array of Points in 3D Space `[[float, float, float], ...]`. +This type alias is mostly made available for internal use and only includes the numpy type. +""" + +Point3D_Array: TypeAlias = Union[ + InternalPoint3D_Array, Tuple[Tuple[float, float, float], ...] +] +""" `shape: (N,3)` An Array of Points in 3D Space `[[float, float, float], ...]`. + +(Please refer to the documentation of the function you are using for further type Information) +""" + +BezierPoints_Array: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,PPC,3)` An Array of Bezier Handles `[[[float, float, float], ...], ...]`. +`PPC` Is the number of points per bezier curve. `N` Is the number of bezier curves. +(Please refer to the documentation of the function you are using for further type Information) +""" + +# Vector Types +Vector3: TypeAlias = npt.NDArray[PointDType] +""" `shape: (3,)` A Vector `[float, float, float]`. """ + +Vector: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,)` A Vector `[float, ...]`. """ + +RowVector: TypeAlias = npt.NDArray[PointDType] +""" `shape: (1,N)` A Row Vector `[[float, ...]]`. """ + +ColVector: TypeAlias = npt.NDArray[PointDType] +""" `shape: (N,1)` A Column Vector `[[float], [float], ...]`. """ + +MatrixMN: TypeAlias = npt.NDArray[PointDType] +""" `shape: (M,N)` A Matrix `[[float, ...], [float, ...], ...]`. """ + +Zeros: TypeAlias = npt.NDArray[ManimFloat] +"""A Matrix of Zeros. Typically created with `numpy.zeros((M,N))`""" + +# Due to current limitations (see https://github.com/python/mypy/issues/14656 / 8263), we don't specify the first argument type (Mobject). +FunctionOverride: TypeAlias = Callable[..., None] +"""Function type returning an animation for the specified Mobject.""" + + +# Misc +PathFuncType: TypeAlias = Callable[[Point3D, Point3D, float], Point3D] +"""Function mapping two points and an alpha value to a new point""" + +MappingFunction: TypeAlias = Callable[[Point3D], Point3D] +"""A function mapping a Point3D to another Point3D""" + +Image: TypeAlias = np.ndarray +"""An Image""" + +StrPath: TypeAlias = "str | PathLike[str]" +StrOrBytesPath: TypeAlias = "str | bytes | PathLike[str] | PathLike[bytes]" diff --git a/manim/utils/bezier.py b/manim/utils/bezier.py index ce5fff437f..938e7362b5 100644 --- a/manim/utils/bezier.py +++ b/manim/utils/bezier.py @@ -2,6 +2,17 @@ from __future__ import annotations +from manim.typing import ( + BezierPoints, + ColVector, + MatrixMN, + Point3D, + Point3D_Array, + PointDType, + QuadraticBezierPoints, + QuadraticBezierPoints_Array, +) + __all__ = [ "bezier", "partial_bezier_points", @@ -20,11 +31,11 @@ ] -import typing from functools import reduce -from typing import Iterable +from typing import Any, Callable, Sequence, overload import numpy as np +import numpy.typing as npt from scipy import linalg from ..utils.simple_functions import choose @@ -32,8 +43,8 @@ def bezier( - points: np.ndarray, -) -> typing.Callable[[float], int | typing.Iterable]: + points: Sequence[Point3D] | Point3D_Array, +) -> Callable[[float], Point3D]: """Classic implementation of a bezier curve. Parameters @@ -43,34 +54,39 @@ def bezier( Returns ------- - typing.Callable[[float], typing.Union[int, typing.Iterable]] function describing the bezier curve. + You can pass a t value between 0 and 1 to get the corresponding point on the curve. """ n = len(points) - 1 - # Cubic Bezier curve if n == 3: - return ( - lambda t: (1 - t) ** 3 * points[0] + return lambda t: np.asarray( + (1 - t) ** 3 * points[0] + 3 * t * (1 - t) ** 2 * points[1] + 3 * (1 - t) * t**2 * points[2] - + t**3 * points[3] + + t**3 * points[3], + dtype=PointDType, ) # Quadratic Bezier curve if n == 2: - return ( - lambda t: (1 - t) ** 2 * points[0] - + 2 * t * (1 - t) * points[1] - + t**2 * points[2] + return lambda t: np.asarray( + (1 - t) ** 2 * points[0] + 2 * t * (1 - t) * points[1] + t**2 * points[2], + dtype=PointDType, ) - return lambda t: sum( - ((1 - t) ** (n - k)) * (t**k) * choose(n, k) * point - for k, point in enumerate(points) + return lambda t: np.asarray( + np.asarray( + [ + (((1 - t) ** (n - k)) * (t**k) * choose(n, k) * point) + for k, point in enumerate(points) + ], + dtype=PointDType, + ).sum(axis=0) ) -def partial_bezier_points(points: np.ndarray, a: float, b: float) -> np.ndarray: +# !TODO: This function has still a weird implementation with the overlapping points +def partial_bezier_points(points: BezierPoints, a: float, b: float) -> BezierPoints: """Given an array of points which define bezier curve, and two numbers 0<=a np.ndarray: np.ndarray Set of points defining the partial bezier curve. """ + _len = len(points) if a == 1: - return [points[-1]] * len(points) + return np.asarray([points[-1]] * _len, dtype=PointDType) - a_to_1 = np.array([bezier(points[i:])(a) for i in range(len(points))]) + a_to_1 = np.asarray( + [bezier(points[i:])(a) for i in range(_len)], + dtype=PointDType, + ) end_prop = (b - a) / (1.0 - a) - return np.array([bezier(a_to_1[: i + 1])(end_prop) for i in range(len(points))]) + return np.asarray( + [bezier(a_to_1[: i + 1])(end_prop) for i in range(_len)], + dtype=PointDType, + ) # Shortened version of partial_bezier_points just for quadratics, # since this is called a fair amount -def partial_quadratic_bezier_points(points, a, b): - points = np.asarray(points, dtype=np.float64) +def partial_quadratic_bezier_points( + points: QuadraticBezierPoints, a: float, b: float +) -> QuadraticBezierPoints: if a == 1: - return 3 * [points[-1]] + return np.asarray(3 * [points[-1]]) - def curve(t): - return ( + def curve(t: float) -> Point3D: + return np.asarray( points[0] * (1 - t) * (1 - t) + 2 * points[1] * t * (1 - t) + points[2] * t * t @@ -118,10 +142,10 @@ def curve(t): h1_prime = (1 - a) * points[1] + a * points[2] end_prop = (b - a) / (1.0 - a) h1 = (1 - end_prop) * h0 + end_prop * h1_prime - return [h0, h1, h2] + return np.asarray((h0, h1, h2)) -def split_quadratic_bezier(points: np.ndarray, t: float) -> np.ndarray: +def split_quadratic_bezier(points: QuadraticBezierPoints, t: float) -> BezierPoints: """Split a quadratic Bézier curve at argument ``t`` into two quadratic curves. Parameters @@ -143,10 +167,10 @@ def split_quadratic_bezier(points: np.ndarray, t: float) -> np.ndarray: s2 = interpolate(h1, a2, t) p = interpolate(s1, s2, t) - return np.array([a1, s1, p, p, s2, a2]) + return np.array((a1, s1, p, p, s2, a2)) -def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray: +def subdivide_quadratic_bezier(points: QuadraticBezierPoints, n: int) -> BezierPoints: """Subdivide a quadratic Bézier curve into ``n`` subcurves which have the same shape. The points at which the curve is split are located at the @@ -178,8 +202,8 @@ def subdivide_quadratic_bezier(points: Iterable[float], n: int) -> np.ndarray: def quadratic_bezier_remap( - triplets: Iterable[Iterable[float]], new_number_of_curves: int -): + triplets: QuadraticBezierPoints_Array, new_number_of_curves: int +) -> QuadraticBezierPoints_Array: """Remaps the number of curves to a higher amount by splitting bezier curves Parameters @@ -234,7 +258,21 @@ def quadratic_bezier_remap( # Linear interpolation variants -def interpolate(start: np.ndarray, end: np.ndarray, alpha: float) -> np.ndarray: + + +@overload +def interpolate(start: float, end: float, alpha: float) -> float: + ... + + +@overload +def interpolate(start: Point3D, end: Point3D, alpha: float) -> Point3D: + ... + + +def interpolate( + start: int | float | Point3D, end: int | float | Point3D, alpha: float | Point3D +) -> float | Point3D: return (1 - alpha) * start + alpha * end @@ -244,52 +282,192 @@ def integer_interpolate( alpha: float, ) -> tuple[int, float]: """ - Alpha is a float between 0 and 1. This returns - an integer between start and end (inclusive) representing - appropriate interpolation between them, along with a - "residue" representing a new proportion between the - returned integer and the next one of the - list. - - For example, if start=0, end=10, alpha=0.46, This - would return (4, 0.6). + This is a variant of interpolate that returns an integer and the residual + + Parameters + ---------- + start + The start of the range + end + The end of the range + alpha + a float between 0 and 1. + + Returns + ------- + tuple[int, float] + This returns an integer between start and end (inclusive) representing + appropriate interpolation between them, along with a + "residue" representing a new proportion between the + returned integer and the next one of the + list. + + Example + ------- + + .. code-block:: pycon + + >>> integer, residue = integer_interpolate(start=0, end=10, alpha=0.46) + >>> np.allclose((integer, residue), (4, 0.6)) + True """ if alpha >= 1: - return (end - 1, 1.0) + return (int(end - 1), 1.0) if alpha <= 0: - return (start, 0) + return (int(start), 0) value = int(interpolate(start, end, alpha)) residue = ((end - start) * alpha) % 1 return (value, residue) +@overload def mid(start: float, end: float) -> float: + ... + + +@overload +def mid(start: Point3D, end: Point3D) -> Point3D: + ... + + +def mid(start: float | Point3D, end: float | Point3D) -> float | Point3D: + """Returns the midpoint between two values. + + Parameters + ---------- + start + The first value + end + The second value + + Returns + ------- + The midpoint between the two values + """ return (start + end) / 2.0 -def inverse_interpolate(start: float, end: float, value: float) -> np.ndarray: +@overload +def inverse_interpolate(start: float, end: float, value: float) -> float: + ... + + +@overload +def inverse_interpolate(start: float, end: float, value: Point3D) -> Point3D: + ... + + +@overload +def inverse_interpolate(start: Point3D, end: Point3D, value: Point3D) -> Point3D: + ... + + +def inverse_interpolate( + start: float | Point3D, end: float | Point3D, value: float | Point3D +) -> float | Point3D: + """Perform inverse interpolation to determine the alpha + values that would produce the specified ``value`` + given the ``start`` and ``end`` values or points. + + Parameters + ---------- + start + The start value or point of the interpolation. + end + The end value or point of the interpolation. + value + The value or point for which the alpha value + should be determined. + + Returns + ------- + The alpha values producing the given input + when interpolating between ``start`` and ``end``. + + Example + ------- + + .. code-block:: pycon + + >>> inverse_interpolate(start=2, end=6, value=4) + 0.5 + + >>> start = np.array([1, 2, 1]) + >>> end = np.array([7, 8, 11]) + >>> value = np.array([4, 5, 5]) + >>> inverse_interpolate(start, end, value) + array([0.5, 0.5, 0.4]) + """ return np.true_divide(value - start, end - start) +@overload def match_interpolate( new_start: float, new_end: float, old_start: float, old_end: float, old_value: float, -) -> np.ndarray: +) -> float: + ... + + +@overload +def match_interpolate( + new_start: float, + new_end: float, + old_start: float, + old_end: float, + old_value: Point3D, +) -> Point3D: + ... + + +def match_interpolate( + new_start: float, + new_end: float, + old_start: float, + old_end: float, + old_value: float | Point3D, +) -> float | Point3D: + """Interpolate a value from an old range to a new range. + + Parameters + ---------- + new_start + The start of the new range. + new_end + The end of the new range. + old_start + The start of the old range. + old_end + The end of the old range. + old_value + The value within the old range whose corresponding + value in the new range (with the same alpha value) + is desired. + + Returns + ------- + The interpolated value within the new range. + + Examples + -------- + >>> match_interpolate(0, 100, 10, 20, 15) + 50.0 + """ + old_alpha = inverse_interpolate(old_start, old_end, old_value) return interpolate( new_start, new_end, - inverse_interpolate(old_start, old_end, old_value), + old_alpha, # type: ignore ) -# Figuring out which bezier curves most smoothly connect a sequence of points - - -def get_smooth_cubic_bezier_handle_points(points): - points = np.array(points) +def get_smooth_cubic_bezier_handle_points( + points: Point3D_Array, +) -> tuple[BezierPoints, BezierPoints]: + points = np.asarray(points) num_handles = len(points) - 1 dim = points.shape[1] if num_handles < 1: @@ -301,7 +479,7 @@ def get_smooth_cubic_bezier_handle_points(points): # diag is a representation of the matrix in diagonal form # See https://www.particleincell.com/2012/bezier-splines/ # for how to arrive at these equations - diag = np.zeros((l + u + 1, 2 * num_handles)) + diag: MatrixMN = np.zeros((l + u + 1, 2 * num_handles)) diag[0, 1::2] = -1 diag[0, 2::2] = 1 diag[1, 0::2] = 2 @@ -314,13 +492,13 @@ def get_smooth_cubic_bezier_handle_points(points): # This is the b as in Ax = b, where we are solving for x, # and A is represented using diag. However, think of entries # to x and b as being points in space, not numbers - b = np.zeros((2 * num_handles, dim)) + b: Point3D_Array = np.zeros((2 * num_handles, dim)) b[1::2] = 2 * points[1:] b[0] = points[0] b[-1] = points[-1] - def solve_func(b): - return linalg.solve_banded((l, u), diag, b) + def solve_func(b: ColVector) -> ColVector | MatrixMN: + return linalg.solve_banded((l, u), diag, b) # type: ignore use_closed_solve_function = is_closed(points) if use_closed_solve_function: @@ -334,8 +512,8 @@ def solve_func(b): b[0] = 2 * points[0] b[-1] = np.zeros(dim) - def closed_curve_solve_func(b): - return linalg.solve(matrix, b) + def closed_curve_solve_func(b: ColVector) -> ColVector | MatrixMN: + return linalg.solve(matrix, b) # type: ignore handle_pairs = np.zeros((2 * num_handles, dim)) for i in range(dim): @@ -347,8 +525,8 @@ def closed_curve_solve_func(b): def get_smooth_handle_points( - points: np.ndarray, -) -> tuple[np.ndarray, np.ndarray]: + points: BezierPoints, +) -> tuple[BezierPoints, BezierPoints]: """Given some anchors (points), compute handles so the resulting bezier curve is smooth. Parameters @@ -362,7 +540,7 @@ def get_smooth_handle_points( Computed handles. """ # NOTE points here are anchors. - points = np.array(points) + points = np.asarray(points) num_handles = len(points) - 1 dim = points.shape[1] if num_handles < 1: @@ -374,7 +552,7 @@ def get_smooth_handle_points( # diag is a representation of the matrix in diagonal form # See https://www.particleincell.com/2012/bezier-splines/ # for how to arrive at these equations - diag = np.zeros((l + u + 1, 2 * num_handles)) + diag: MatrixMN = np.zeros((l + u + 1, 2 * num_handles)) diag[0, 1::2] = -1 diag[0, 2::2] = 1 diag[1, 0::2] = 2 @@ -392,8 +570,8 @@ def get_smooth_handle_points( b[0] = points[0] b[-1] = points[-1] - def solve_func(b: np.ndarray) -> np.ndarray: - return linalg.solve_banded((l, u), diag, b) + def solve_func(b: ColVector) -> ColVector | MatrixMN: + return linalg.solve_banded((l, u), diag, b) # type: ignore use_closed_solve_function = is_closed(points) if use_closed_solve_function: @@ -407,8 +585,8 @@ def solve_func(b: np.ndarray) -> np.ndarray: b[0] = 2 * points[0] b[-1] = np.zeros(dim) - def closed_curve_solve_func(b: np.ndarray) -> np.ndarray: - return linalg.solve(matrix, b) + def closed_curve_solve_func(b: ColVector) -> ColVector | MatrixMN: + return linalg.solve(matrix, b) # type: ignore handle_pairs = np.zeros((2 * num_handles, dim)) for i in range(dim): @@ -419,7 +597,9 @@ def closed_curve_solve_func(b: np.ndarray) -> np.ndarray: return handle_pairs[0::2], handle_pairs[1::2] -def diag_to_matrix(l_and_u: tuple[int, int], diag: np.ndarray) -> np.ndarray: +def diag_to_matrix( + l_and_u: tuple[int, int], diag: npt.NDArray[Any] +) -> npt.NDArray[Any]: """ Converts array whose rows represent diagonal entries of a matrix into the matrix itself. @@ -438,7 +618,9 @@ def diag_to_matrix(l_and_u: tuple[int, int], diag: np.ndarray) -> np.ndarray: # Given 4 control points for a cubic bezier curve (or arrays of such) # return control points for 2 quadratics (or 2n quadratics) approximating them. -def get_quadratic_approximation_of_cubic(a0, h0, h1, a1): +def get_quadratic_approximation_of_cubic( + a0: Point3D, h0: Point3D, h1: Point3D, a1: Point3D +) -> BezierPoints: a0 = np.array(a0, ndmin=2) h0 = np.array(h0, ndmin=2) h1 = np.array(h1, ndmin=2) @@ -486,9 +668,9 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1): m, n = a0.shape t_mid = t_mid.repeat(n).reshape((m, n)) - # Compute bezier point and tangent at the chosen value of t - mid = bezier([a0, h0, h1, a1])(t_mid) - Tm = bezier([h0 - a0, h1 - h0, a1 - h1])(t_mid) + # Compute bezier point and tangent at the chosen value of t (these are vectorized) + mid = bezier([a0, h0, h1, a1])(t_mid) # type: ignore + Tm = bezier([h0 - a0, h1 - h0, a1 - h1])(t_mid) # type: ignore # Intersection between tangent lines at end points # and tangent in the middle @@ -506,15 +688,15 @@ def get_quadratic_approximation_of_cubic(a0, h0, h1, a1): return result -def is_closed(points: tuple[np.ndarray, np.ndarray]) -> bool: - return np.allclose(points[0], points[-1]) +def is_closed(points: Point3D_Array) -> bool: + return np.allclose(points[0], points[-1]) # type: ignore def proportions_along_bezier_curve_for_point( - point: typing.Iterable[float | int], - control_points: typing.Iterable[typing.Iterable[float | int]], - round_to: float | int | None = 1e-6, -) -> np.ndarray: + point: Point3D, + control_points: BezierPoints, + round_to: float = 1e-6, +) -> npt.NDArray[Any]: """Obtains the proportion along the bezier curve corresponding to a given point given the bezier curve's control points. @@ -583,21 +765,23 @@ def proportions_along_bezier_curve_for_point( # Roots will be none, but in this specific instance, we don't need to consider that. continue bezier_polynom = np.polynomial.Polynomial(terms[::-1]) - polynom_roots = bezier_polynom.roots() + polynom_roots = bezier_polynom.roots() # type: ignore if len(polynom_roots) > 0: polynom_roots = np.around(polynom_roots, int(np.log10(1 / round_to))) roots.append(polynom_roots) roots = [[root for root in rootlist if root.imag == 0] for rootlist in roots] - roots = reduce(np.intersect1d, roots) # Get common roots. - roots = np.array([r.real for r in roots if 0 <= r.real <= 1]) - return roots + # Get common roots + # arg-type: ignore + roots = reduce(np.intersect1d, roots) # type: ignore + result = np.asarray([r.real for r in roots if 0 <= r.real <= 1]) + return result def point_lies_on_bezier( - point: typing.Iterable[float | int], - control_points: typing.Iterable[typing.Iterable[float | int]], - round_to: float | int | None = 1e-6, + point: Point3D, + control_points: BezierPoints, + round_to: float = 1e-6, ) -> bool: """Checks if a given point lies on the bezier curves with the given control points. diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 2994504ce5..cdd75ad9ae 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -3,43 +3,55 @@ This module contains the implementation of :class:`.ManimColor`, the data structure internally used to represent colors. -""" +The preferred way of using these colors is by importing their constants from manim: -from __future__ import annotations +.. code-block:: pycon -# logger = _config.logger -import colorsys -import random -from typing import Any, Sequence, Union + >>> from manim import RED, GREEN, BLUE + >>> print(RED) + #FC6255 -import numpy as np -from typing_extensions import Literal, TypeAlias +Note this way uses the name of the colors in UPPERCASE. -from ...utils.space_ops import normalize +.. note:: -ManimColorDType: TypeAlias = np.float64 -ManimFloat: TypeAlias = np.float64 -ManimInt: TypeAlias = np.int64 + The colors of type "C" have an alias equal to the colorname without a letter, + e.g. GREEN = GREEN_C +""" -RGB_Array_Float: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimFloat]]" -RGB_Tuple_Float: TypeAlias = "tuple[float, float, float]" +from __future__ import annotations -RGB_Array_Int: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimInt]]" -RGB_Tuple_Int: TypeAlias = "tuple[int, int, int]" +import colorsys -RGBA_Array_Float: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimFloat]]" -RGBA_Tuple_Float: TypeAlias = "tuple[float, float, float, float]" +# logger = _config.logger +import random +import re +from typing import Any, Sequence, TypeVar, Union, overload -RGBA_Array_Int: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimInt]]" -RGBA_Tuple_Int: TypeAlias = "tuple[int, int, int, int]" +import numpy as np +import numpy.typing as npt +from typing_extensions import Self, TypeAlias + +from manim.typing import ( + HSV_Array_Float, + HSV_Tuple_Float, + ManimColorDType, + ManimColorInternal, + RGB_Array_Float, + RGB_Array_Int, + RGB_Tuple_Float, + RGB_Tuple_Int, + RGBA_Array_Float, + RGBA_Array_Int, + RGBA_Tuple_Float, + RGBA_Tuple_Int, +) -HSV_Array_Float: TypeAlias = RGB_Array_Float -HSV_Tuple_Float: TypeAlias = RGB_Tuple_Float +from ...utils.space_ops import normalize -ManimColorInternal: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimColorDType]]" +# import manim._config as _config -import re re_hex = re.compile("((?<=#)|(?<=0x))[A-F0-9]{6,8}", re.IGNORECASE) @@ -83,7 +95,7 @@ class ManimColor: def __init__( self, - value: ParsableManimColor, + value: ParsableManimColor | None, alpha: float = 1.0, ) -> None: if value is None: @@ -514,7 +526,7 @@ def from_rgb( cls, rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int, alpha: float = 1.0, - ) -> ManimColor: + ) -> Self: """Creates a ManimColor from an RGB Array. Automagically decides which type it is int/float .. warning:: @@ -539,7 +551,7 @@ def from_rgb( @classmethod def from_rgba( cls, rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int - ) -> ManimColor: + ) -> Self: """Creates a ManimColor from an RGBA Array. Automagically decides which type it is int/float .. warning:: @@ -559,7 +571,7 @@ def from_rgba( return cls(rgba) @classmethod - def from_hex(cls, hex: str, alpha: float = 1.0) -> ManimColor: + def from_hex(cls, hex: str, alpha: float = 1.0) -> Self: """Creates a Manim Color from a hex string, prefixes allowed # and 0x Parameters @@ -579,7 +591,7 @@ def from_hex(cls, hex: str, alpha: float = 1.0) -> ManimColor: @classmethod def from_hsv( cls, hsv: HSV_Array_Float | HSV_Tuple_Float, alpha: float = 1.0 - ) -> ManimColor: + ) -> Self: """Creates a ManimColor from an HSV Array Parameters @@ -597,12 +609,30 @@ def from_hsv( rgb = colorsys.hsv_to_rgb(*hsv) return cls(rgb, alpha) + @overload + @classmethod + def parse( + cls, + color: ParsableManimColor | None, + alpha: float = ..., + ) -> Self: + ... + + @overload + @classmethod + def parse( + cls, + color: Sequence[ParsableManimColor], + alpha: float = ..., + ) -> list[Self]: + ... + @classmethod def parse( cls, color: ParsableManimColor | list[ParsableManimColor] | None, alpha: float = 1.0, - ) -> ManimColor | list[ManimColor]: + ) -> Self | list[Self]: """ Handles the parsing of a list of colors or a single color. @@ -688,6 +718,9 @@ def __xor__(self, other: ManimColor) -> ManimColor: """ParsableManimColor is the representation for all types that are parsable to a color in manim""" +ManimColorT = TypeVar("ManimColorT", bound=ManimColor) + + def color_to_rgb(color: ParsableManimColor) -> RGB_Array_Float: """Helper function for use in functional style programming refer to :meth:`to_rgb` in :class:`ManimColor` @@ -722,12 +755,12 @@ def color_to_rgba(color: ParsableManimColor, alpha: float = 1) -> RGBA_Array_Flo return ManimColor(color).to_rgba_with_alpha(alpha) -def color_to_int_rgb(color: ManimColor) -> RGB_Array_Int: +def color_to_int_rgb(color: ParsableManimColor) -> RGB_Array_Int: """Helper function for use in functional style programming refer to :meth:`to_int_rgb` in :class:`ManimColor` Parameters ---------- - color : ManimColor + color : ParsableManimColor A color Returns @@ -738,12 +771,12 @@ def color_to_int_rgb(color: ManimColor) -> RGB_Array_Int: return ManimColor(color).to_int_rgb() -def color_to_int_rgba(color: ManimColor, alpha: float = 1.0) -> RGBA_Array_Int: +def color_to_int_rgba(color: ParsableManimColor, alpha: float = 1.0) -> RGBA_Array_Int: """Helper function for use in functional style programming refer to :meth:`to_int_rgba_with_alpha` in :class:`ManimColor` Parameters ---------- - color : ManimColor + color : ParsableManimColor A color alpha : float, optional alpha value to be used in the color, by default 1.0 @@ -756,7 +789,9 @@ def color_to_int_rgba(color: ManimColor, alpha: float = 1.0) -> RGBA_Array_Int: return ManimColor(color).to_int_rgba_with_alpha(alpha) -def rgb_to_color(rgb: RGB_Array_Float | RGB_Tuple_Float) -> ManimColor: +def rgb_to_color( + rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int, +) -> ManimColor: """Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor` Parameters @@ -772,7 +807,9 @@ def rgb_to_color(rgb: RGB_Array_Float | RGB_Tuple_Float) -> ManimColor: return ManimColor.from_rgb(rgb) -def rgba_to_color(rgba: RGBA_Array_Float | RGBA_Tuple_Float) -> ManimColor: +def rgba_to_color( + rgba: RGBA_Array_Float | RGBA_Tuple_Float | RGBA_Array_Int | RGBA_Tuple_Int, +) -> ManimColor: """Helper function for use in functional style programming refer to :meth:`from_rgba` in :class:`ManimColor` Parameters @@ -788,7 +825,9 @@ def rgba_to_color(rgba: RGBA_Array_Float | RGBA_Tuple_Float) -> ManimColor: return ManimColor.from_rgba(rgba) -def rgb_to_hex(rgb: RGB_Array_Float | RGB_Tuple_Float) -> str: +def rgb_to_hex( + rgb: RGB_Array_Float | RGB_Tuple_Float | RGB_Array_Int | RGB_Tuple_Int, +) -> str: """Helper function for use in functional style programming refer to :meth:`from_rgb` in :class:`ManimColor` Parameters @@ -820,7 +859,7 @@ def hex_to_rgb(hex_code: str) -> RGB_Array_Float: return ManimColor(hex_code).to_rgb() -def invert_color(color: ManimColor) -> ManimColor: +def invert_color(color: ManimColorT) -> ManimColorT: """Helper function for use in functional style programming refer to :meth:`invert` in :class:`ManimColor` Parameters @@ -837,15 +876,15 @@ def invert_color(color: ManimColor) -> ManimColor: def interpolate_arrays( - arr1: np.ndarray[Any, Any], arr2: np.ndarray[Any, Any], alpha: float + arr1: npt.NDArray[Any], arr2: npt.NDArray[Any], alpha: float ) -> np.ndarray: """Helper function used in Manim to fade between two objects smoothly Parameters ---------- - arr1 : np.ndarray[Any, Any] + arr1 : npt.NDArray[Any] The first array of colors - arr2 : np.ndarray[Any, Any] + arr2 : npt.NDArray[Any] The second array of colors alpha : float The alpha value corresponding to the interpolation point between the two inputs @@ -880,7 +919,7 @@ def color_gradient( return ManimColor(reference_colors[0]) if len(reference_colors) == 1: return [ManimColor(reference_colors[0])] * length_of_output - rgbs = list(map(color_to_rgb, reference_colors)) + rgbs = [color_to_rgb(color) for color in reference_colors] alphas = np.linspace(0, (len(rgbs) - 1), length_of_output) floors = alphas.astype("int") alphas_mod1 = alphas % 1 @@ -894,8 +933,8 @@ def color_gradient( def interpolate_color( - color1: ManimColor, color2: ManimColor, alpha: float -) -> ManimColor: + color1: ManimColorT, color2: ManimColor, alpha: float +) -> ManimColorT: """Standalone function to interpolate two ManimColors and get the result refer to :meth:`interpolate` in :class:`ManimColor` Parameters @@ -915,7 +954,7 @@ def interpolate_color( return color1.interpolate(color2, alpha) -def average_color(*colors: ManimColor) -> ManimColor: +def average_color(*colors: ParsableManimColor) -> ManimColor: """Determines the Average color of the given parameters Returns @@ -923,7 +962,7 @@ def average_color(*colors: ManimColor) -> ManimColor: ManimColor The average color of the input """ - rgbs = np.array(list(map(color_to_rgb, colors))) + rgbs = np.array([color_to_rgb(color) for color in colors]) mean_rgb = np.apply_along_axis(np.mean, 0, rgbs) return rgb_to_color(mean_rgb) @@ -939,8 +978,7 @@ def random_bright_color() -> ManimColor: ManimColor A bright ManimColor """ - color = random_color() - curr_rgb = color_to_rgb(color) + curr_rgb = color_to_rgb(random_color()) new_rgb = interpolate_arrays(curr_rgb, np.ones(len(curr_rgb)), 0.5) return ManimColor(new_rgb) @@ -962,10 +1000,10 @@ def random_color() -> ManimColor: def get_shaded_rgb( - rgb: np.ndarray, - point: np.ndarray, - unit_normal_vect: np.ndarray, - light_source: np.ndarray, + rgb: npt.NDArray[Any], + point: npt.NDArray[Any], + unit_normal_vect: npt.NDArray[Any], + light_source: npt.NDArray[Any], ) -> RGBA_Array_Float: to_sun = normalize(light_source - point) factor = 0.5 * np.dot(unit_normal_vect, to_sun) ** 3 diff --git a/manim/utils/color/manim_colors.py b/manim/utils/color/manim_colors.py index f36925e035..42c93985e6 100644 --- a/manim/utils/color/manim_colors.py +++ b/manim/utils/color/manim_colors.py @@ -125,87 +125,87 @@ def named_lines_group(length, colors, names, text_colors, align_to_block): from .core import ManimColor -WHITE: ManimColor = ManimColor("#FFFFFF") -GRAY_A: ManimColor = ManimColor("#DDDDDD") -GREY_A: ManimColor = ManimColor("#DDDDDD") -GRAY_B: ManimColor = ManimColor("#BBBBBB") -GREY_B: ManimColor = ManimColor("#BBBBBB") -GRAY_C: ManimColor = ManimColor("#888888") -GREY_C: ManimColor = ManimColor("#888888") -GRAY_D: ManimColor = ManimColor("#444444") -GREY_D: ManimColor = ManimColor("#444444") -GRAY_E: ManimColor = ManimColor("#222222") -GREY_E: ManimColor = ManimColor("#222222") -BLACK: ManimColor = ManimColor("#000000") -LIGHTER_GRAY: ManimColor = ManimColor("#DDDDDD") -LIGHTER_GREY: ManimColor = ManimColor("#DDDDDD") -LIGHT_GRAY: ManimColor = ManimColor("#BBBBBB") -LIGHT_GREY: ManimColor = ManimColor("#BBBBBB") -GRAY: ManimColor = ManimColor("#888888") -GREY: ManimColor = ManimColor("#888888") -DARK_GRAY: ManimColor = ManimColor("#444444") -DARK_GREY: ManimColor = ManimColor("#444444") -DARKER_GRAY: ManimColor = ManimColor("#222222") -DARKER_GREY: ManimColor = ManimColor("#222222") -BLUE_A: ManimColor = ManimColor("#C7E9F1") -BLUE_B: ManimColor = ManimColor("#9CDCEB") -BLUE_C: ManimColor = ManimColor("#58C4DD") -BLUE_D: ManimColor = ManimColor("#29ABCA") -BLUE_E: ManimColor = ManimColor("#236B8E") -PURE_BLUE: ManimColor = ManimColor("#0000FF") -BLUE: ManimColor = ManimColor("#58C4DD") -DARK_BLUE: ManimColor = ManimColor("#236B8E") -TEAL_A: ManimColor = ManimColor("#ACEAD7") -TEAL_B: ManimColor = ManimColor("#76DDC0") -TEAL_C: ManimColor = ManimColor("#5CD0B3") -TEAL_D: ManimColor = ManimColor("#55C1A7") -TEAL_E: ManimColor = ManimColor("#49A88F") -TEAL: ManimColor = ManimColor("#5CD0B3") -GREEN_A: ManimColor = ManimColor("#C9E2AE") -GREEN_B: ManimColor = ManimColor("#A6CF8C") -GREEN_C: ManimColor = ManimColor("#83C167") -GREEN_D: ManimColor = ManimColor("#77B05D") -GREEN_E: ManimColor = ManimColor("#699C52") -PURE_GREEN: ManimColor = ManimColor("#00FF00") -GREEN: ManimColor = ManimColor("#83C167") -YELLOW_A: ManimColor = ManimColor("#FFF1B6") -YELLOW_B: ManimColor = ManimColor("#FFEA94") -YELLOW_C: ManimColor = ManimColor("#FFFF00") -YELLOW_D: ManimColor = ManimColor("#F4D345") -YELLOW_E: ManimColor = ManimColor("#E8C11C") -YELLOW: ManimColor = ManimColor("#FFFF00") -GOLD_A: ManimColor = ManimColor("#F7C797") -GOLD_B: ManimColor = ManimColor("#F9B775") -GOLD_C: ManimColor = ManimColor("#F0AC5F") -GOLD_D: ManimColor = ManimColor("#E1A158") -GOLD_E: ManimColor = ManimColor("#C78D46") -GOLD: ManimColor = ManimColor("#F0AC5F") -RED_A: ManimColor = ManimColor("#F7A1A3") -RED_B: ManimColor = ManimColor("#FF8080") -RED_C: ManimColor = ManimColor("#FC6255") -RED_D: ManimColor = ManimColor("#E65A4C") -RED_E: ManimColor = ManimColor("#CF5044") -PURE_RED: ManimColor = ManimColor("#FF0000") -RED: ManimColor = ManimColor("#FC6255") -MAROON_A: ManimColor = ManimColor("#ECABC1") -MAROON_B: ManimColor = ManimColor("#EC92AB") -MAROON_C: ManimColor = ManimColor("#C55F73") -MAROON_D: ManimColor = ManimColor("#A24D61") -MAROON_E: ManimColor = ManimColor("#94424F") -MAROON: ManimColor = ManimColor("#C55F73") -PURPLE_A: ManimColor = ManimColor("#CAA3E8") -PURPLE_B: ManimColor = ManimColor("#B189C6") -PURPLE_C: ManimColor = ManimColor("#9A72AC") -PURPLE_D: ManimColor = ManimColor("#715582") -PURPLE_E: ManimColor = ManimColor("#644172") -PURPLE: ManimColor = ManimColor("#9A72AC") -PINK: ManimColor = ManimColor("#D147BD") -LIGHT_PINK: ManimColor = ManimColor("#DC75CD") -ORANGE: ManimColor = ManimColor("#FF862F") -LIGHT_BROWN: ManimColor = ManimColor("#CD853F") -DARK_BROWN: ManimColor = ManimColor("#8B4513") -GRAY_BROWN: ManimColor = ManimColor("#736357") -GREY_BROWN: ManimColor = ManimColor("#736357") +WHITE = ManimColor("#FFFFFF") +GRAY_A = ManimColor("#DDDDDD") +GREY_A = ManimColor("#DDDDDD") +GRAY_B = ManimColor("#BBBBBB") +GREY_B = ManimColor("#BBBBBB") +GRAY_C = ManimColor("#888888") +GREY_C = ManimColor("#888888") +GRAY_D = ManimColor("#444444") +GREY_D = ManimColor("#444444") +GRAY_E = ManimColor("#222222") +GREY_E = ManimColor("#222222") +BLACK = ManimColor("#000000") +LIGHTER_GRAY = ManimColor("#DDDDDD") +LIGHTER_GREY = ManimColor("#DDDDDD") +LIGHT_GRAY = ManimColor("#BBBBBB") +LIGHT_GREY = ManimColor("#BBBBBB") +GRAY = ManimColor("#888888") +GREY = ManimColor("#888888") +DARK_GRAY = ManimColor("#444444") +DARK_GREY = ManimColor("#444444") +DARKER_GRAY = ManimColor("#222222") +DARKER_GREY = ManimColor("#222222") +BLUE_A = ManimColor("#C7E9F1") +BLUE_B = ManimColor("#9CDCEB") +BLUE_C = ManimColor("#58C4DD") +BLUE_D = ManimColor("#29ABCA") +BLUE_E = ManimColor("#236B8E") +PURE_BLUE = ManimColor("#0000FF") +BLUE = ManimColor("#58C4DD") +DARK_BLUE = ManimColor("#236B8E") +TEAL_A = ManimColor("#ACEAD7") +TEAL_B = ManimColor("#76DDC0") +TEAL_C = ManimColor("#5CD0B3") +TEAL_D = ManimColor("#55C1A7") +TEAL_E = ManimColor("#49A88F") +TEAL = ManimColor("#5CD0B3") +GREEN_A = ManimColor("#C9E2AE") +GREEN_B = ManimColor("#A6CF8C") +GREEN_C = ManimColor("#83C167") +GREEN_D = ManimColor("#77B05D") +GREEN_E = ManimColor("#699C52") +PURE_GREEN = ManimColor("#00FF00") +GREEN = ManimColor("#83C167") +YELLOW_A = ManimColor("#FFF1B6") +YELLOW_B = ManimColor("#FFEA94") +YELLOW_C = ManimColor("#FFFF00") +YELLOW_D = ManimColor("#F4D345") +YELLOW_E = ManimColor("#E8C11C") +YELLOW = ManimColor("#FFFF00") +GOLD_A = ManimColor("#F7C797") +GOLD_B = ManimColor("#F9B775") +GOLD_C = ManimColor("#F0AC5F") +GOLD_D = ManimColor("#E1A158") +GOLD_E = ManimColor("#C78D46") +GOLD = ManimColor("#F0AC5F") +RED_A = ManimColor("#F7A1A3") +RED_B = ManimColor("#FF8080") +RED_C = ManimColor("#FC6255") +RED_D = ManimColor("#E65A4C") +RED_E = ManimColor("#CF5044") +PURE_RED = ManimColor("#FF0000") +RED = ManimColor("#FC6255") +MAROON_A = ManimColor("#ECABC1") +MAROON_B = ManimColor("#EC92AB") +MAROON_C = ManimColor("#C55F73") +MAROON_D = ManimColor("#A24D61") +MAROON_E = ManimColor("#94424F") +MAROON = ManimColor("#C55F73") +PURPLE_A = ManimColor("#CAA3E8") +PURPLE_B = ManimColor("#B189C6") +PURPLE_C = ManimColor("#9A72AC") +PURPLE_D = ManimColor("#715582") +PURPLE_E = ManimColor("#644172") +PURPLE = ManimColor("#9A72AC") +PINK = ManimColor("#D147BD") +LIGHT_PINK = ManimColor("#DC75CD") +ORANGE = ManimColor("#FF862F") +LIGHT_BROWN = ManimColor("#CD853F") +DARK_BROWN = ManimColor("#8B4513") +GRAY_BROWN = ManimColor("#736357") +GREY_BROWN = ManimColor("#736357") # Colors used for Manim Community's logo and banner diff --git a/manim/utils/space_ops.py b/manim/utils/space_ops.py index f24360cc75..16af964e74 100644 --- a/manim/utils/space_ops.py +++ b/manim/utils/space_ops.py @@ -2,6 +2,8 @@ from __future__ import annotations +from manim.typing import Point3D_Array, Vector + __all__ = [ "quaternion_mult", "quaternion_from_angle_axis", @@ -37,7 +39,6 @@ import itertools as it -import math from typing import Sequence import numpy as np @@ -108,7 +109,7 @@ def quaternion_from_angle_axis( """ if not axis_normalized: axis = normalize(axis) - return [math.cos(angle / 2), *(math.sin(angle / 2) * axis)] + return [np.cos(angle / 2), *(np.sin(angle / 2) * axis)] def angle_axis_from_quaternion(quaternion: Sequence[float]) -> Sequence[float]: @@ -256,7 +257,7 @@ def rotation_about_z(angle: float) -> np.ndarray: np.ndarray Gives back the rotated matrix. """ - c, s = math.cos(angle), math.sin(angle) + c, s = np.cos(angle), np.sin(angle) return np.array( [ [c, -s, 0], @@ -540,10 +541,10 @@ def line_intersection( def find_intersection( - p0s: Sequence[np.ndarray], - v0s: Sequence[np.ndarray], - p1s: Sequence[np.ndarray], - v1s: Sequence[np.ndarray], + p0s: Sequence[np.ndarray] | Point3D_Array, + v0s: Sequence[np.ndarray] | Point3D_Array, + p1s: Sequence[np.ndarray] | Point3D_Array, + v1s: Sequence[np.ndarray] | Point3D_Array, threshold: float = 1e-5, ) -> Sequence[np.ndarray]: """ @@ -621,7 +622,38 @@ def shoelace_direction(x_y: np.ndarray) -> str: return "CW" if area > 0 else "CCW" -def cross2d(a, b): +def cross2d( + a: Sequence[Vector] | Vector, b: Sequence[Vector] | Vector +) -> Sequence[float] | float: + """Compute the determinant(s) of the passed + vector (sequences). + + Parameters + ---------- + a + A vector or a sequence of vectors. + b + A vector or a sequence of vectors. + + Returns + ------- + Sequence[float] | float + The determinant or sequence of determinants + of the first two components of the specified + vectors. + + Examples + -------- + .. code-block:: pycon + + >>> cross2d(np.array([1, 2]), np.array([3, 4])) + -2 + >>> cross2d( + ... np.array([[1, 2, 0], [1, 0, 0]]), + ... np.array([[3, 4, 0], [0, 1, 0]]), + ... ) + array([-2, 1]) + """ if len(a.shape) == 2: return a[:, 0] * b[:, 1] - a[:, 1] * b[:, 0] else: diff --git a/.mypy.ini b/mypy.ini similarity index 51% rename from .mypy.ini rename to mypy.ini index 48cad442f4..a4707261b7 100644 --- a/.mypy.ini +++ b/mypy.ini @@ -1,34 +1,90 @@ [mypy] -show_error_codes = True +strict = False +files = manim +python_version = 3.10 +; plugins = numpy.typing.mypy_plugin +ignore_errors = False +cache_fine_grained = True +warn_unused_ignores = True + +# Disallow Dynamic Typing +# disallow_any_unimported = True +# disallow_any_expr = False +# disallow_any_decorated = True +# disallow_any_explicit = True +# disallow_any_generics = True +# disallow_subclassing_any = True +# +# # Disallow Untyped Defs and Calls +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +# check_untyped_defs = False +# disallow_untyped_decorators = True +# +# # None and Optional Handling +# implicit_optional = False +# strict_optional = True +# +# # Configuring Warnings +# warn_redundant_casts = True +# warn_unused_ignores = True +warn_return_any = True +# warn_unreachable = True +# +# # Strictness Flags +# allow_untyped_globals = False +# allow_redefinition = False +# local_partial_types = False +# strict_equality = True +# +# # Configuring Error Messages +# show_error_context = True +# show_column_numbers = True +# show_error_codes = True +# pretty = True +# color_output = True +# error_summary = True +# +# disable_recursive_aliases = True -# ignore most files; should be checked once proper types have been implemented -[mypy-manim.__main__] +[mypy-manim._config.*] +ignore_errors = True + +[mypy-manim.animation.*] ignore_errors = True [mypy-manim.camera.*] ignore_errors = True -[mypy-manim.scene.*] +[mypy-manim.cli.*] ignore_errors = True [mypy-manim.cli.cfg.*] ignore_errors = True +[mypy-manim.gui.*] +ignore_errors = True + [mypy-manim.mobject.*] ignore_errors = True -[mypy-manim._config.*] +[mypy-manim.plugins.*] ignore_errors = True -[mypy-manim.utils.*] +[mypy-manim.renderer.*] ignore_errors = True -[mypy-manim.utils.color] -ignore_errors = False +[mypy-manim.scene.*] +ignore_errors = True -[mypy-manim.animation.*] +[mypy-manim.utils.*] +ignore_errors = True + +[mypy-manim.__main__] ignore_errors = True + # ---------------- We can't properly type this ------------------------ [mypy-manim.grpc.*] @@ -40,10 +96,6 @@ ignore_errors = True [mypy-manimpango] ignore_missing_imports = True -# Has stubs in 3.8 -[mypy-numpy] -ignore_missing_imports = True - # Has stubs in 3.8 [mypy-pydub] ignore_missing_imports = True @@ -66,9 +118,6 @@ ignore_missing_imports = True [mypy-moderngl_window.*] ignore_missing_imports = True -[mypy-colour] -ignore_missing_imports = True - [mypy-dearpygui.*] ignore_missing_imports = True diff --git a/poetry.lock b/poetry.lock index 611d37fa59..0ded9d0217 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,9 +1,10 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "aiofiles" version = "22.1.0" description = "File support for asyncio." +category = "main" optional = true python-versions = ">=3.7,<4.0" files = [ @@ -15,6 +16,7 @@ files = [ name = "aiosqlite" version = "0.19.0" description = "asyncio bridge to the standard sqlite3 module" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -30,6 +32,7 @@ docs = ["sphinx (==6.1.3)", "sphinx-mdinclude (==0.5.3)"] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -41,6 +44,7 @@ files = [ name = "anyio" version = "4.0.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -62,6 +66,7 @@ trio = ["trio (>=0.22)"] name = "appnope" version = "0.1.3" description = "Disable App Nap on macOS >= 10.9" +category = "main" optional = true python-versions = "*" files = [ @@ -73,6 +78,7 @@ files = [ name = "argon2-cffi" version = "23.1.0" description = "Argon2 for Python" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -93,6 +99,7 @@ typing = ["mypy"] name = "argon2-cffi-bindings" version = "21.2.0" description = "Low-level CFFI bindings for Argon2" +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -130,6 +137,7 @@ tests = ["pytest"] name = "arrow" version = "1.3.0" description = "Better dates & times for Python" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -143,12 +151,13 @@ types-python-dateutil = ">=2.8.10" [package.extras] doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] +test = ["dateparser (>=1.0.0,<2.0.0)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (>=3.0.0,<4.0.0)"] [[package]] name = "astor" version = "0.8.1" description = "Read/rewrite/write Python ASTs" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" files = [ @@ -160,6 +169,7 @@ files = [ name = "astroid" version = "2.15.8" description = "An abstract syntax tree for Python with inference support." +category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -179,6 +189,7 @@ wrapt = [ name = "asttokens" version = "2.4.1" description = "Annotate AST trees with source code positions" +category = "main" optional = true python-versions = "*" files = [ @@ -197,6 +208,7 @@ test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -215,6 +227,7 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte name = "babel" version = "2.13.1" description = "Internationalization utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -232,6 +245,7 @@ dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] name = "backcall" version = "0.2.0" description = "Specifications for callback functions passed in to an API" +category = "main" optional = true python-versions = "*" files = [ @@ -243,6 +257,7 @@ files = [ name = "beautifulsoup4" version = "4.12.2" description = "Screen-scraping library" +category = "main" optional = false python-versions = ">=3.6.0" files = [ @@ -261,6 +276,7 @@ lxml = ["lxml"] name = "black" version = "23.10.1" description = "The uncompromising code formatter." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -303,6 +319,7 @@ uvloop = ["uvloop (>=0.15.2)"] name = "bleach" version = "6.1.0" description = "An easy safelist-based HTML-sanitizing tool." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -321,6 +338,7 @@ css = ["tinycss2 (>=1.1.0,<1.3)"] name = "certifi" version = "2023.7.22" description = "Python package for providing Mozilla's CA Bundle." +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -332,6 +350,7 @@ files = [ name = "cffi" version = "1.16.0" description = "Foreign Function Interface for Python calling C code." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -396,6 +415,7 @@ pycparser = "*" name = "cfgv" version = "3.4.0" description = "Validate configuration and produce human readable error messages." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -407,6 +427,7 @@ files = [ name = "charset-normalizer" version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -506,6 +527,7 @@ files = [ name = "click" version = "8.1.7" description = "Composable command line interface toolkit" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -520,6 +542,7 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""} name = "click-default-group" version = "1.2.4" description = "click_default_group" +category = "main" optional = false python-versions = ">=2.7" files = [ @@ -537,6 +560,7 @@ test = ["pytest"] name = "cloup" version = "2.1.2" description = "Adds features to Click: option groups, constraints, subcommand sections and help themes." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -552,6 +576,7 @@ typing-extensions = {version = "*", markers = "python_version <= \"3.8\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -563,6 +588,7 @@ files = [ name = "comm" version = "0.1.4" description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -582,6 +608,7 @@ typing = ["mypy (>=0.990)"] name = "commonmark" version = "0.9.1" description = "Python parser for the CommonMark Markdown spec" +category = "dev" optional = false python-versions = "*" files = [ @@ -596,6 +623,7 @@ test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] name = "contourpy" version = "1.1.1" description = "Python library for calculating contours of 2D quadrilateral grids" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -667,6 +695,7 @@ test-no-images = ["pytest", "pytest-cov", "wurlitzer"] name = "coverage" version = "7.3.2" description = "Code coverage measurement for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -734,6 +763,7 @@ toml = ["tomli"] name = "cryptography" version = "41.0.5" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -779,6 +809,7 @@ test-randomorder = ["pytest-randomly"] name = "cycler" version = "0.12.1" description = "Composable style cycles" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -794,6 +825,7 @@ tests = ["pytest", "pytest-cov", "pytest-xdist"] name = "cython" version = "3.0.5" description = "The Cython compiler for writing C extensions in the Python language." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -861,6 +893,7 @@ files = [ name = "data-science-types" version = "0.2.23" description = "Type stubs for Python machine learning libraries" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -875,6 +908,7 @@ dev = ["black", "flake8", "flake8-pyi", "matplotlib", "mypy (==0.770)", "numpy", name = "dearpygui" version = "1.10.1" description = "DearPyGui: A simple Python GUI Toolkit" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -902,6 +936,7 @@ files = [ name = "debugpy" version = "1.8.0" description = "An implementation of the Debug Adapter Protocol for Python" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -929,6 +964,7 @@ files = [ name = "decorator" version = "5.1.1" description = "Decorators for Humans" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -940,6 +976,7 @@ files = [ name = "defusedxml" version = "0.7.1" description = "XML bomb protection for Python stdlib modules" +category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -951,6 +988,7 @@ files = [ name = "deprecated" version = "1.2.14" description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -968,6 +1006,7 @@ dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] name = "dill" version = "0.3.7" description = "serialize all of Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -982,6 +1021,7 @@ graph = ["objgraph (>=1.7.2)"] name = "distlib" version = "0.3.7" description = "Distribution utilities" +category = "dev" optional = false python-versions = "*" files = [ @@ -993,6 +1033,7 @@ files = [ name = "docutils" version = "0.17.1" description = "Docutils -- Python Documentation Utilities" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -1000,10 +1041,23 @@ files = [ {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, ] +[[package]] +name = "entrypoints" +version = "0.4" +description = "Discover and load entry points from installed packages." +category = "main" +optional = true +python-versions = ">=3.6" +files = [ + {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, + {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, +] + [[package]] name = "exceptiongroup" version = "1.1.3" description = "Backport of PEP 654 (exception groups)" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1018,6 +1072,7 @@ test = ["pytest (>=6)"] name = "execnet" version = "2.0.2" description = "execnet: rapid multi-Python deployment" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1032,6 +1087,7 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] name = "executing" version = "2.0.1" description = "Get the currently executing AST node of a frame, and other information" +category = "main" optional = true python-versions = ">=3.5" files = [ @@ -1046,6 +1102,7 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth name = "fastjsonschema" version = "2.18.1" description = "Fastest Python implementation of JSON schema" +category = "main" optional = true python-versions = "*" files = [ @@ -1060,6 +1117,7 @@ devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benc name = "filelock" version = "3.13.1" description = "A platform independent file lock." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1076,6 +1134,7 @@ typing = ["typing-extensions (>=4.8)"] name = "flake8" version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -1092,6 +1151,7 @@ pyflakes = ">=2.3.0,<2.4.0" name = "flake8-bugbear" version = "21.11.29" description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1110,6 +1170,7 @@ dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"] name = "flake8-builtins" version = "1.5.3" description = "Check for python builtins being used as variables or parameters." +category = "dev" optional = false python-versions = "*" files = [ @@ -1127,6 +1188,7 @@ test = ["coverage", "coveralls", "mock", "pytest", "pytest-cov"] name = "flake8-comprehensions" version = "3.14.0" description = "A flake8 plugin to help you write better list/set/dict comprehensions." +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1141,6 +1203,7 @@ flake8 = ">=3.0,<3.2.0 || >3.2.0" name = "flake8-docstrings" version = "1.7.0" description = "Extension for flake8 which uses pydocstyle to check docstrings" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1156,6 +1219,7 @@ pydocstyle = ">=2.1" name = "flake8-plugin-utils" version = "1.3.3" description = "The package provides base classes and utils for flake8 plugin writing" +category = "dev" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1167,6 +1231,7 @@ files = [ name = "flake8-pytest-style" version = "1.7.2" description = "A flake8 plugin checking common style issues or inconsistencies with pytest-based tests." +category = "dev" optional = false python-versions = ">=3.7.2,<4.0.0" files = [ @@ -1181,6 +1246,7 @@ flake8-plugin-utils = ">=1.3.2,<2.0.0" name = "flake8-rst-docstrings" version = "0.2.7" description = "Python docstring reStructuredText (RST) validator" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1197,6 +1263,7 @@ restructuredtext-lint = "*" name = "flake8-simplify" version = "0.14.6" description = "flake8 plugin which checks for code that can be simplified" +category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -1212,6 +1279,7 @@ flake8 = ">=3.7" name = "fonttools" version = "4.44.0" description = "Tools to manipulate font files" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1277,6 +1345,7 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] name = "fqdn" version = "1.5.1" description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" +category = "main" optional = true python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" files = [ @@ -1288,6 +1357,7 @@ files = [ name = "furo" version = "2022.9.29" description = "A clean customisable Sphinx documentation theme." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1305,6 +1375,7 @@ sphinx-basic-ng = "*" name = "gitdb" version = "4.0.11" description = "Git Object Database" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1319,6 +1390,7 @@ smmap = ">=3.0.1,<6" name = "gitpython" version = "3.1.40" description = "GitPython is a Python library used to interact with Git repositories" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1336,6 +1408,7 @@ test = ["black", "coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre name = "glcontext" version = "2.5.0" description = "Portable OpenGL Context" +category = "main" optional = false python-versions = "*" files = [ @@ -1411,6 +1484,7 @@ files = [ name = "identify" version = "2.5.31" description = "File identification library for Python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -1425,6 +1499,7 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1436,6 +1511,7 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -1447,6 +1523,7 @@ files = [ name = "importlib-metadata" version = "6.8.0" description = "Read metadata from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1466,6 +1543,7 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs name = "importlib-resources" version = "6.1.0" description = "Read resources from Python packages" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1484,6 +1562,7 @@ testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1495,6 +1574,7 @@ files = [ name = "ipykernel" version = "6.26.0" description = "IPython Kernel for Jupyter" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1508,7 +1588,7 @@ comm = ">=0.1.1" debugpy = ">=1.6.5" ipython = ">=7.23.1" jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" matplotlib-inline = ">=0.1" nest-asyncio = "*" packaging = "*" @@ -1528,6 +1608,7 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio" name = "ipython" version = "8.12.3" description = "IPython: Productive Interactive Computing" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1567,6 +1648,7 @@ test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.21)", "pa name = "ipython-genutils" version = "0.2.0" description = "Vestigial utilities from IPython" +category = "main" optional = true python-versions = "*" files = [ @@ -1578,6 +1660,7 @@ files = [ name = "isoduration" version = "20.11.0" description = "Operations with ISO 8601 durations" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1592,6 +1675,7 @@ arrow = ">=0.15.0" name = "isort" version = "5.12.0" description = "A Python utility / library to sort Python imports." +category = "dev" optional = false python-versions = ">=3.8.0" files = [ @@ -1609,6 +1693,7 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "isosurfaces" version = "0.1.0" description = "Construct isolines/isosurfaces over a 2D/3D scalar field defined by a function (not a uniform grid)" +category = "main" optional = false python-versions = "*" files = [ @@ -1623,6 +1708,7 @@ numpy = "*" name = "jedi" version = "0.19.1" description = "An autocompletion tool for Python that can be used for text editors." +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -1642,6 +1728,7 @@ testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1659,6 +1746,7 @@ i18n = ["Babel (>=2.7)"] name = "json5" version = "0.9.14" description = "A Python implementation of the JSON5 data format." +category = "main" optional = true python-versions = "*" files = [ @@ -1673,6 +1761,7 @@ dev = ["hypothesis"] name = "jsonpointer" version = "2.4" description = "Identify specific nodes in a JSON document (RFC 6901)" +category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" files = [ @@ -1684,6 +1773,7 @@ files = [ name = "jsonschema" version = "4.19.2" description = "An implementation of JSON Schema validation for Python" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1715,6 +1805,7 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "jsonschema-specifications" version = "2023.7.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1728,31 +1819,34 @@ referencing = ">=0.28.0" [[package]] name = "jupyter-client" -version = "8.5.0" +version = "7.4.9" description = "Jupyter protocol implementation and client libraries" +category = "main" optional = true -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "jupyter_client-8.5.0-py3-none-any.whl", hash = "sha256:c3877aac7257ec68d79b5c622ce986bd2a992ca42f6ddc9b4dd1da50e89f7028"}, - {file = "jupyter_client-8.5.0.tar.gz", hash = "sha256:e8754066510ce456358df363f97eae64b50860f30dc1fe8c6771440db3be9a63"}, + {file = "jupyter_client-7.4.9-py3-none-any.whl", hash = "sha256:214668aaea208195f4c13d28eb272ba79f945fc0cf3f11c7092c20b2ca1980e7"}, + {file = "jupyter_client-7.4.9.tar.gz", hash = "sha256:52be28e04171f07aed8f20e1616a5a552ab9fee9cbbe6c1896ae170c3880d392"}, ] [package.dependencies] -importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.10\""} -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +entrypoints = "*" +jupyter-core = ">=4.9.2" +nest-asyncio = ">=1.5.4" python-dateutil = ">=2.8.2" pyzmq = ">=23.0" tornado = ">=6.2" -traitlets = ">=5.3" +traitlets = "*" [package.extras] -docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] +doc = ["ipykernel", "myst-parser", "sphinx (>=1.3.6)", "sphinx-rtd-theme", "sphinxcontrib-github-alt"] +test = ["codecov", "coverage", "ipykernel (>=6.12)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] [[package]] name = "jupyter-core" version = "5.5.0" description = "Jupyter core package. A base package on which Jupyter projects rely." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1773,6 +1867,7 @@ test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] name = "jupyter-events" version = "0.8.0" description = "Jupyter Event System library" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1798,6 +1893,7 @@ test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "p name = "jupyter-server" version = "2.9.1" description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1810,7 +1906,7 @@ anyio = ">=3.1.0" argon2-cffi = "*" jinja2 = "*" jupyter-client = ">=7.4.4" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" jupyter-events = ">=0.6.0" jupyter-server-terminals = "*" nbconvert = ">=6.4.4" @@ -1834,6 +1930,7 @@ test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0)", "pytest-console-sc name = "jupyter-server-fileid" version = "0.9.0" description = "" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1853,6 +1950,7 @@ test = ["jupyter-server[test] (>=1.15,<3)", "pytest", "pytest-cov"] name = "jupyter-server-terminals" version = "0.4.4" description = "A Jupyter Server Extension Providing Terminals." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1872,6 +1970,7 @@ test = ["coverage", "jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-cov", name = "jupyter-server-ydoc" version = "0.8.0" description = "A Jupyter Server Extension Providing Y Documents." +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1891,6 +1990,7 @@ test = ["coverage", "jupyter-server[test] (>=2.0.0a0)", "pytest (>=7.0)", "pytes name = "jupyter-ydoc" version = "0.2.5" description = "Document structures for collaborative editing using Ypy" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1910,6 +2010,7 @@ test = ["pre-commit", "pytest", "pytest-asyncio", "websockets (>=10.0)", "ypy-we name = "jupyterlab" version = "3.6.6" description = "JupyterLab computational environment" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1938,6 +2039,7 @@ test = ["check-manifest", "coverage", "jupyterlab-server[test]", "pre-commit", " name = "jupyterlab-pygments" version = "0.2.2" description = "Pygments theme using JupyterLab CSS variables" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -1949,6 +2051,7 @@ files = [ name = "jupyterlab-server" version = "2.25.0" description = "A set of server components for JupyterLab and JupyterLab like applications." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -1975,6 +2078,7 @@ test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-v name = "kiwisolver" version = "1.4.5" description = "A fast implementation of the Cassowary constraint solver" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2088,6 +2192,7 @@ files = [ name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2133,6 +2238,7 @@ files = [ name = "manimpango" version = "0.5.0" description = "Bindings for Pango for using with Manim." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2158,6 +2264,7 @@ files = [ name = "mapbox-earcut" version = "1.0.1" description = "Python bindings for the mapbox earcut C++ polygon triangulation library." +category = "main" optional = false python-versions = "*" files = [ @@ -2232,6 +2339,7 @@ test = ["pytest"] name = "markdown-it-py" version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2256,6 +2364,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2279,16 +2388,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -2325,6 +2424,7 @@ files = [ name = "matplotlib" version = "3.7.3" description = "Python plotting package" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -2394,6 +2494,7 @@ setuptools_scm = ">=7" name = "matplotlib-inline" version = "0.1.6" description = "Inline Matplotlib backend for Jupyter" +category = "main" optional = true python-versions = ">=3.5" files = [ @@ -2408,6 +2509,7 @@ traitlets = "*" name = "mccabe" version = "0.6.1" description = "McCabe checker, plugin for flake8" +category = "dev" optional = false python-versions = "*" files = [ @@ -2419,6 +2521,7 @@ files = [ name = "mdit-py-plugins" version = "0.3.5" description = "Collection of plugins for markdown-it-py" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2438,6 +2541,7 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2449,6 +2553,7 @@ files = [ name = "mistune" version = "3.0.2" description = "A sane and fast Markdown parser with useful plugins and renderers" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2460,6 +2565,7 @@ files = [ name = "moderngl" version = "5.8.2" description = "ModernGL: High performance rendering for Python 3" +category = "main" optional = false python-versions = "*" files = [ @@ -2522,6 +2628,7 @@ glcontext = ">=2.3.6,<3" name = "moderngl-window" version = "2.4.4" description = "A cross platform helper library for ModernGL making window creation and resource loading simple" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2550,6 +2657,7 @@ trimesh = ["scipy (>=1.3.2)", "trimesh (>=3.2.6,<4)"] name = "multipledispatch" version = "1.0.0" description = "Multiple dispatch" +category = "main" optional = false python-versions = "*" files = [ @@ -2557,48 +2665,11 @@ files = [ {file = "multipledispatch-1.0.0.tar.gz", hash = "sha256:5c839915465c68206c3e9c473357908216c28383b425361e5d144594bf85a7e0"}, ] -[[package]] -name = "mypy" -version = "0.931" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, - {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, - {file = "mypy-0.931-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:300717a07ad09525401a508ef5d105e6b56646f7942eb92715a1c8d610149714"}, - {file = "mypy-0.931-cp310-cp310-win_amd64.whl", hash = "sha256:7b3f6f557ba4afc7f2ce6d3215d5db279bcf120b3cfd0add20a5d4f4abdae5bc"}, - {file = "mypy-0.931-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:1bf752559797c897cdd2c65f7b60c2b6969ffe458417b8d947b8340cc9cec08d"}, - {file = "mypy-0.931-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4365c60266b95a3f216a3047f1d8e3f895da6c7402e9e1ddfab96393122cc58d"}, - {file = "mypy-0.931-cp36-cp36m-win_amd64.whl", hash = "sha256:1b65714dc296a7991000b6ee59a35b3f550e0073411ac9d3202f6516621ba66c"}, - {file = "mypy-0.931-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e839191b8da5b4e5d805f940537efcaa13ea5dd98418f06dc585d2891d228cf0"}, - {file = "mypy-0.931-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:50c7346a46dc76a4ed88f3277d4959de8a2bd0a0fa47fa87a4cde36fe247ac05"}, - {file = "mypy-0.931-cp37-cp37m-win_amd64.whl", hash = "sha256:d8f1ff62f7a879c9fe5917b3f9eb93a79b78aad47b533911b853a757223f72e7"}, - {file = "mypy-0.931-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9fe20d0872b26c4bba1c1be02c5340de1019530302cf2dcc85c7f9fc3252ae0"}, - {file = "mypy-0.931-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1b06268df7eb53a8feea99cbfff77a6e2b205e70bf31743e786678ef87ee8069"}, - {file = "mypy-0.931-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8c11003aaeaf7cc2d0f1bc101c1cc9454ec4cc9cb825aef3cafff8a5fdf4c799"}, - {file = "mypy-0.931-cp38-cp38-win_amd64.whl", hash = "sha256:d9d2b84b2007cea426e327d2483238f040c49405a6bf4074f605f0156c91a47a"}, - {file = "mypy-0.931-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ff3bf387c14c805ab1388185dd22d6b210824e164d4bb324b195ff34e322d166"}, - {file = "mypy-0.931-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b56154f8c09427bae082b32275a21f500b24d93c88d69a5e82f3978018a0266"}, - {file = "mypy-0.931-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ca7f8c4b1584d63c9a0f827c37ba7a47226c19a23a753d52e5b5eddb201afcd"}, - {file = "mypy-0.931-cp39-cp39-win_amd64.whl", hash = "sha256:74f7eccbfd436abe9c352ad9fb65872cc0f1f0a868e9d9c44db0893440f0c697"}, - {file = "mypy-0.931-py3-none-any.whl", hash = "sha256:1171f2e0859cfff2d366da2c7092b06130f232c636a3f7301e3feb8b41f6377d"}, - {file = "mypy-0.931.tar.gz", hash = "sha256:0038b21890867793581e4cb0d810829f5fd4441aa75796b53033af3aa30430ce"}, -] - -[package.dependencies] -mypy-extensions = ">=0.4.3" -tomli = ">=1.1.0" -typing-extensions = ">=3.10" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -python2 = ["typed-ast (>=1.4.0,<2)"] - [[package]] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -2610,6 +2681,7 @@ files = [ name = "myst-parser" version = "0.17.2" description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2636,6 +2708,7 @@ testing = ["beautifulsoup4", "coverage", "docutils (>=0.17.0,<0.18.0)", "pytest name = "nbclassic" version = "1.0.0" description = "Jupyter Notebook as a Jupyter Server extension." +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2671,6 +2744,7 @@ test = ["coverage", "nbval", "pytest", "pytest-cov", "pytest-jupyter", "pytest-p name = "nbclient" version = "0.8.0" description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." +category = "main" optional = true python-versions = ">=3.8.0" files = [ @@ -2680,7 +2754,7 @@ files = [ [package.dependencies] jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" +jupyter-core = ">=4.12,<5.0.0 || >=5.1.0" nbformat = ">=5.1" traitlets = ">=5.4" @@ -2693,6 +2767,7 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>= name = "nbconvert" version = "7.10.0" description = "Converting Jupyter Notebooks" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2731,6 +2806,7 @@ webpdf = ["playwright"] name = "nbformat" version = "5.9.2" description = "The Jupyter Notebook format" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -2752,6 +2828,7 @@ test = ["pep440", "pre-commit", "pytest", "testpath"] name = "nest-asyncio" version = "1.5.8" description = "Patch asyncio to allow nested event loops" +category = "main" optional = true python-versions = ">=3.5" files = [ @@ -2763,6 +2840,7 @@ files = [ name = "networkx" version = "3.1" description = "Python package for creating and manipulating graphs and networks" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2781,6 +2859,7 @@ test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "nodeenv" version = "1.8.0" description = "Node.js virtual environment builder" +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -2793,13 +2872,14 @@ setuptools = "*" [[package]] name = "notebook" -version = "6.5.4" +version = "6.5.6" description = "A web-based notebook environment for interactive computing" +category = "main" optional = true python-versions = ">=3.7" files = [ - {file = "notebook-6.5.4-py3-none-any.whl", hash = "sha256:dd17e78aefe64c768737b32bf171c1c766666a21cc79a44d37a1700771cab56f"}, - {file = "notebook-6.5.4.tar.gz", hash = "sha256:517209568bd47261e2def27a140e97d49070602eea0d226a696f42a7f16c9a4e"}, + {file = "notebook-6.5.6-py3-none-any.whl", hash = "sha256:c1e2eb2e3b6079a0552a04974883a48d04c3c05792170d64a4b23d707d453181"}, + {file = "notebook-6.5.6.tar.gz", hash = "sha256:b4625a4b7a597839dd3156b140d5ba2c7123761f98245a3290f67a8b8ee048d9"}, ] [package.dependencies] @@ -2807,14 +2887,14 @@ argon2-cffi = "*" ipykernel = "*" ipython-genutils = "*" jinja2 = "*" -jupyter-client = ">=5.3.4" +jupyter-client = ">=5.3.4,<8" jupyter-core = ">=4.6.1" nbclassic = ">=0.4.7" nbconvert = ">=5" nbformat = "*" nest-asyncio = ">=1.5" prometheus-client = "*" -pyzmq = ">=17" +pyzmq = ">=17,<25" Send2Trash = ">=1.8.0" terminado = ">=0.8.3" tornado = ">=6.1" @@ -2829,6 +2909,7 @@ test = ["coverage", "nbval", "pytest", "pytest-cov", "requests", "requests-unixs name = "notebook-shim" version = "0.2.3" description = "A shim layer for notebook traits and config" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -2846,6 +2927,7 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" name = "numpy" version = "1.24.4" description = "Fundamental package for array computing in Python" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -2883,6 +2965,7 @@ files = [ name = "overrides" version = "7.4.0" description = "A decorator to automatically detect mismatch when overriding a method." +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2894,6 +2977,7 @@ files = [ name = "packaging" version = "23.2" description = "Core utilities for Python packages" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -2905,6 +2989,7 @@ files = [ name = "pandocfilters" version = "1.5.0" description = "Utilities for writing pandoc filters in python" +category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -2916,6 +3001,7 @@ files = [ name = "parso" version = "0.8.3" description = "A Python Parser" +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -2931,6 +3017,7 @@ testing = ["docopt", "pytest (<6.0.0)"] name = "pathspec" version = "0.11.2" description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -2942,6 +3029,7 @@ files = [ name = "pexpect" version = "4.8.0" description = "Pexpect allows easy control of interactive console applications." +category = "main" optional = true python-versions = "*" files = [ @@ -2956,6 +3044,7 @@ ptyprocess = ">=0.5" name = "pickleshare" version = "0.7.5" description = "Tiny 'shelve'-like database with concurrency support" +category = "main" optional = true python-versions = "*" files = [ @@ -2967,6 +3056,7 @@ files = [ name = "pillow" version = "9.5.0" description = "Python Imaging Library (Fork)" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3046,6 +3136,7 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3057,6 +3148,7 @@ files = [ name = "platformdirs" version = "3.11.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3072,6 +3164,7 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-co name = "pluggy" version = "1.3.0" description = "plugin and hook calling mechanisms for python" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -3087,6 +3180,7 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3105,6 +3199,7 @@ virtualenv = ">=20.10.0" name = "prometheus-client" version = "0.18.0" description = "Python client for the Prometheus monitoring system." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3119,6 +3214,7 @@ twisted = ["twisted"] name = "prompt-toolkit" version = "3.0.39" description = "Library for building powerful interactive command lines in Python" +category = "main" optional = true python-versions = ">=3.7.0" files = [ @@ -3133,6 +3229,7 @@ wcwidth = "*" name = "psutil" version = "5.9.6" description = "Cross-platform lib for process and system monitoring in Python." +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -3161,6 +3258,7 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] name = "psutil-wheels" version = "5.8.0" description = "Cross-platform lib for process and system monitoring in Python." +category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3186,6 +3284,7 @@ test = ["enum34", "ipaddress", "mock", "pywin32", "unittest2", "wmi"] name = "ptyprocess" version = "0.7.0" description = "Run a subprocess in a pseudo terminal" +category = "main" optional = true python-versions = "*" files = [ @@ -3197,6 +3296,7 @@ files = [ name = "pure-eval" version = "0.2.2" description = "Safely evaluate AST nodes without side effects" +category = "main" optional = true python-versions = "*" files = [ @@ -3211,6 +3311,7 @@ tests = ["pytest"] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3222,6 +3323,7 @@ files = [ name = "pycairo" version = "1.25.1" description = "Python interface for cairo" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3246,6 +3348,7 @@ files = [ name = "pycodestyle" version = "2.7.0" description = "Python style guide checker" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3257,6 +3360,7 @@ files = [ name = "pycparser" version = "2.21" description = "C parser in Python" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3268,6 +3372,7 @@ files = [ name = "pydocstyle" version = "6.3.0" description = "Python docstring style checker" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3285,6 +3390,7 @@ toml = ["tomli (>=1.2.3)"] name = "pydub" version = "0.25.1" description = "Manipulate audio with an simple and easy high level interface" +category = "main" optional = false python-versions = "*" files = [ @@ -3296,6 +3402,7 @@ files = [ name = "pyflakes" version = "2.3.1" description = "passive checker of Python programs" +category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -3307,6 +3414,7 @@ files = [ name = "pygithub" version = "1.59.1" description = "Use the full Github API v3" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3324,6 +3432,7 @@ requests = ">=2.14.0" name = "pyglet" version = "2.0.9" description = "Cross-platform windowing and multimedia library" +category = "main" optional = false python-versions = "*" files = [ @@ -3335,6 +3444,7 @@ files = [ name = "pygments" version = "2.16.1" description = "Pygments is a syntax highlighting package written in Python." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3349,6 +3459,7 @@ plugins = ["importlib-metadata"] name = "pyjwt" version = "2.8.0" description = "JSON Web Token implementation in Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3369,6 +3480,7 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] name = "pylint" version = "2.17.7" description = "python code static checker" +category = "dev" optional = false python-versions = ">=3.7.2" files = [ @@ -3398,6 +3510,7 @@ testutils = ["gitpython (>3)"] name = "pynacl" version = "1.5.0" description = "Python binding to the Networking and Cryptography (NaCl) library" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3424,6 +3537,7 @@ tests = ["hypothesis (>=3.27.0)", "pytest (>=3.2.1,!=3.3.0)"] name = "pyobjc-core" version = "10.0" description = "Python<->ObjC Interoperability Module" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3440,6 +3554,7 @@ files = [ name = "pyobjc-framework-cocoa" version = "10.0" description = "Wrappers for the Cocoa frameworks on macOS" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -3459,6 +3574,7 @@ pyobjc-core = ">=10.0" name = "pyparsing" version = "3.1.1" description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" optional = false python-versions = ">=3.6.8" files = [ @@ -3473,6 +3589,7 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pyrr" version = "0.10.3" description = "3D mathematical functions using NumPy" +category = "main" optional = false python-versions = "*" files = [ @@ -3488,6 +3605,7 @@ numpy = "*" name = "pytest" version = "7.4.3" description = "pytest: simple powerful testing with Python" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3510,6 +3628,7 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no name = "pytest-cov" version = "3.0.0" description = "Pytest plugin for measuring coverage." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3528,6 +3647,7 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtuale name = "pytest-forked" version = "1.6.0" description = "run tests in isolated forked subprocesses" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -3543,6 +3663,7 @@ pytest = ">=3.10" name = "pytest-xdist" version = "2.5.0" description = "pytest xdist plugin for distributed testing and loop-on-failing modes" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -3564,6 +3685,7 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -3578,6 +3700,7 @@ six = ">=1.5" name = "python-json-logger" version = "2.0.7" description = "A python library adding a json log formatter" +category = "main" optional = true python-versions = ">=3.6" files = [ @@ -3589,6 +3712,7 @@ files = [ name = "pytz" version = "2023.3.post1" description = "World timezone definitions, modern and historical" +category = "main" optional = false python-versions = "*" files = [ @@ -3600,6 +3724,7 @@ files = [ name = "pywin32" version = "306" description = "Python for Window Extensions" +category = "main" optional = true python-versions = "*" files = [ @@ -3623,6 +3748,7 @@ files = [ name = "pywinpty" version = "2.0.12" description = "Pseudo terminal support for Windows from Python." +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3638,6 +3764,7 @@ files = [ name = "pyyaml" version = "6.0.1" description = "YAML parser and emitter for Python" +category = "main" optional = false python-versions = ">=3.6" files = [ @@ -3695,113 +3822,97 @@ files = [ [[package]] name = "pyzmq" -version = "25.1.1" +version = "24.0.1" description = "Python bindings for 0MQ" +category = "main" optional = true python-versions = ">=3.6" files = [ - {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:381469297409c5adf9a0e884c5eb5186ed33137badcbbb0560b86e910a2f1e76"}, - {file = "pyzmq-25.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:955215ed0604dac5b01907424dfa28b40f2b2292d6493445dd34d0dfa72586a8"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:985bbb1316192b98f32e25e7b9958088431d853ac63aca1d2c236f40afb17c83"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:afea96f64efa98df4da6958bae37f1cbea7932c35878b185e5982821bc883369"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76705c9325d72a81155bb6ab48d4312e0032bf045fb0754889133200f7a0d849"}, - {file = "pyzmq-25.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77a41c26205d2353a4c94d02be51d6cbdf63c06fbc1295ea57dad7e2d3381b71"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:12720a53e61c3b99d87262294e2b375c915fea93c31fc2336898c26d7aed34cd"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:57459b68e5cd85b0be8184382cefd91959cafe79ae019e6b1ae6e2ba8a12cda7"}, - {file = "pyzmq-25.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:292fe3fc5ad4a75bc8df0dfaee7d0babe8b1f4ceb596437213821f761b4589f9"}, - {file = "pyzmq-25.1.1-cp310-cp310-win32.whl", hash = "sha256:35b5ab8c28978fbbb86ea54958cd89f5176ce747c1fb3d87356cf698048a7790"}, - {file = "pyzmq-25.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:11baebdd5fc5b475d484195e49bae2dc64b94a5208f7c89954e9e354fc609d8f"}, - {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:d20a0ddb3e989e8807d83225a27e5c2eb2260eaa851532086e9e0fa0d5287d83"}, - {file = "pyzmq-25.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e1c1be77bc5fb77d923850f82e55a928f8638f64a61f00ff18a67c7404faf008"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d89528b4943d27029a2818f847c10c2cecc79fa9590f3cb1860459a5be7933eb"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:90f26dc6d5f241ba358bef79be9ce06de58d477ca8485e3291675436d3827cf8"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2b92812bd214018e50b6380ea3ac0c8bb01ac07fcc14c5f86a5bb25e74026e9"}, - {file = "pyzmq-25.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f957ce63d13c28730f7fd6b72333814221c84ca2421298f66e5143f81c9f91f"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:047a640f5c9c6ade7b1cc6680a0e28c9dd5a0825135acbd3569cc96ea00b2505"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7f7e58effd14b641c5e4dec8c7dab02fb67a13df90329e61c869b9cc607ef752"}, - {file = "pyzmq-25.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c2910967e6ab16bf6fbeb1f771c89a7050947221ae12a5b0b60f3bca2ee19bca"}, - {file = "pyzmq-25.1.1-cp311-cp311-win32.whl", hash = "sha256:76c1c8efb3ca3a1818b837aea423ff8a07bbf7aafe9f2f6582b61a0458b1a329"}, - {file = "pyzmq-25.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:44e58a0554b21fc662f2712814a746635ed668d0fbc98b7cb9d74cb798d202e6"}, - {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e1ffa1c924e8c72778b9ccd386a7067cddf626884fd8277f503c48bb5f51c762"}, - {file = "pyzmq-25.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1af379b33ef33757224da93e9da62e6471cf4a66d10078cf32bae8127d3d0d4a"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cff084c6933680d1f8b2f3b4ff5bbb88538a4aac00d199ac13f49d0698727ecb"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2400a94f7dd9cb20cd012951a0cbf8249e3d554c63a9c0cdfd5cbb6c01d2dec"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d81f1ddae3858b8299d1da72dd7d19dd36aab654c19671aa8a7e7fb02f6638a"}, - {file = "pyzmq-25.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:255ca2b219f9e5a3a9ef3081512e1358bd4760ce77828e1028b818ff5610b87b"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a882ac0a351288dd18ecae3326b8a49d10c61a68b01419f3a0b9a306190baf69"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:724c292bb26365659fc434e9567b3f1adbdb5e8d640c936ed901f49e03e5d32e"}, - {file = "pyzmq-25.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ca1ed0bb2d850aa8471387882247c68f1e62a4af0ce9c8a1dbe0d2bf69e41fb"}, - {file = "pyzmq-25.1.1-cp312-cp312-win32.whl", hash = "sha256:b3451108ab861040754fa5208bca4a5496c65875710f76789a9ad27c801a0075"}, - {file = "pyzmq-25.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:eadbefd5e92ef8a345f0525b5cfd01cf4e4cc651a2cffb8f23c0dd184975d787"}, - {file = "pyzmq-25.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:db0b2af416ba735c6304c47f75d348f498b92952f5e3e8bff449336d2728795d"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c133e93b405eb0d36fa430c94185bdd13c36204a8635470cccc200723c13bb"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:273bc3959bcbff3f48606b28229b4721716598d76b5aaea2b4a9d0ab454ec062"}, - {file = "pyzmq-25.1.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cbc8df5c6a88ba5ae385d8930da02201165408dde8d8322072e3e5ddd4f68e22"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:18d43df3f2302d836f2a56f17e5663e398416e9dd74b205b179065e61f1a6edf"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:73461eed88a88c866656e08f89299720a38cb4e9d34ae6bf5df6f71102570f2e"}, - {file = "pyzmq-25.1.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c850ce7976d19ebe7b9d4b9bb8c9dfc7aac336c0958e2651b88cbd46682123"}, - {file = "pyzmq-25.1.1-cp36-cp36m-win32.whl", hash = "sha256:d2045d6d9439a0078f2a34b57c7b18c4a6aef0bee37f22e4ec9f32456c852c71"}, - {file = "pyzmq-25.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:458dea649f2f02a0b244ae6aef8dc29325a2810aa26b07af8374dc2a9faf57e3"}, - {file = "pyzmq-25.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7cff25c5b315e63b07a36f0c2bab32c58eafbe57d0dce61b614ef4c76058c115"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1579413ae492b05de5a6174574f8c44c2b9b122a42015c5292afa4be2507f28"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3d0a409d3b28607cc427aa5c30a6f1e4452cc44e311f843e05edb28ab5e36da0"}, - {file = "pyzmq-25.1.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21eb4e609a154a57c520e3d5bfa0d97e49b6872ea057b7c85257b11e78068222"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:034239843541ef7a1aee0c7b2cb7f6aafffb005ede965ae9cbd49d5ff4ff73cf"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f8115e303280ba09f3898194791a153862cbf9eef722ad8f7f741987ee2a97c7"}, - {file = "pyzmq-25.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a5d26fe8f32f137e784f768143728438877d69a586ddeaad898558dc971a5ae"}, - {file = "pyzmq-25.1.1-cp37-cp37m-win32.whl", hash = "sha256:f32260e556a983bc5c7ed588d04c942c9a8f9c2e99213fec11a031e316874c7e"}, - {file = "pyzmq-25.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:abf34e43c531bbb510ae7e8f5b2b1f2a8ab93219510e2b287a944432fad135f3"}, - {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:87e34f31ca8f168c56d6fbf99692cc8d3b445abb5bfd08c229ae992d7547a92a"}, - {file = "pyzmq-25.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c9c6c9b2c2f80747a98f34ef491c4d7b1a8d4853937bb1492774992a120f475d"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5619f3f5a4db5dbb572b095ea3cb5cc035335159d9da950830c9c4db2fbb6995"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a34d2395073ef862b4032343cf0c32a712f3ab49d7ec4f42c9661e0294d106f"}, - {file = "pyzmq-25.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25f0e6b78220aba09815cd1f3a32b9c7cb3e02cb846d1cfc526b6595f6046618"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3669cf8ee3520c2f13b2e0351c41fea919852b220988d2049249db10046a7afb"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2d163a18819277e49911f7461567bda923461c50b19d169a062536fffe7cd9d2"}, - {file = "pyzmq-25.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:df27ffddff4190667d40de7beba4a950b5ce78fe28a7dcc41d6f8a700a80a3c0"}, - {file = "pyzmq-25.1.1-cp38-cp38-win32.whl", hash = "sha256:a382372898a07479bd34bda781008e4a954ed8750f17891e794521c3e21c2e1c"}, - {file = "pyzmq-25.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:52533489f28d62eb1258a965f2aba28a82aa747202c8fa5a1c7a43b5db0e85c1"}, - {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:03b3f49b57264909aacd0741892f2aecf2f51fb053e7d8ac6767f6c700832f45"}, - {file = "pyzmq-25.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:330f9e188d0d89080cde66dc7470f57d1926ff2fb5576227f14d5be7ab30b9fa"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2ca57a5be0389f2a65e6d3bb2962a971688cbdd30b4c0bd188c99e39c234f414"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d457aed310f2670f59cc5b57dcfced452aeeed77f9da2b9763616bd57e4dbaae"}, - {file = "pyzmq-25.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c56d748ea50215abef7030c72b60dd723ed5b5c7e65e7bc2504e77843631c1a6"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f03d3f0d01cb5a018debeb412441996a517b11c5c17ab2001aa0597c6d6882c"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:820c4a08195a681252f46926de10e29b6bbf3e17b30037bd4250d72dd3ddaab8"}, - {file = "pyzmq-25.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17ef5f01d25b67ca8f98120d5fa1d21efe9611604e8eb03a5147360f517dd1e2"}, - {file = "pyzmq-25.1.1-cp39-cp39-win32.whl", hash = "sha256:04ccbed567171579ec2cebb9c8a3e30801723c575601f9a990ab25bcac6b51e2"}, - {file = "pyzmq-25.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:e61f091c3ba0c3578411ef505992d356a812fb200643eab27f4f70eed34a29ef"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ade6d25bb29c4555d718ac6d1443a7386595528c33d6b133b258f65f963bb0f6"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0c95ddd4f6e9fca4e9e3afaa4f9df8552f0ba5d1004e89ef0a68e1f1f9807c7"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48e466162a24daf86f6b5ca72444d2bf39a5e58da5f96370078be67c67adc978"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abc719161780932c4e11aaebb203be3d6acc6b38d2f26c0f523b5b59d2fc1996"}, - {file = "pyzmq-25.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ccf825981640b8c34ae54231b7ed00271822ea1c6d8ba1090ebd4943759abf5"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c2f20ce161ebdb0091a10c9ca0372e023ce24980d0e1f810f519da6f79c60800"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:deee9ca4727f53464daf089536e68b13e6104e84a37820a88b0a057b97bba2d2"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aa8d6cdc8b8aa19ceb319aaa2b660cdaccc533ec477eeb1309e2a291eaacc43a"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:019e59ef5c5256a2c7378f2fb8560fc2a9ff1d315755204295b2eab96b254d0a"}, - {file = "pyzmq-25.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:b9af3757495c1ee3b5c4e945c1df7be95562277c6e5bccc20a39aec50f826cd0"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:548d6482dc8aadbe7e79d1b5806585c8120bafa1ef841167bc9090522b610fa6"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:057e824b2aae50accc0f9a0570998adc021b372478a921506fddd6c02e60308e"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2243700cc5548cff20963f0ca92d3e5e436394375ab8a354bbea2b12911b20b0"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79986f3b4af059777111409ee517da24a529bdbd46da578b33f25580adcff728"}, - {file = "pyzmq-25.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:11d58723d44d6ed4dd677c5615b2ffb19d5c426636345567d6af82be4dff8a55"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:49d238cf4b69652257db66d0c623cd3e09b5d2e9576b56bc067a396133a00d4a"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fedbdc753827cf014c01dbbee9c3be17e5a208dcd1bf8641ce2cd29580d1f0d4"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bc16ac425cc927d0a57d242589f87ee093884ea4804c05a13834d07c20db203c"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11c1d2aed9079c6b0c9550a7257a836b4a637feb334904610f06d70eb44c56d2"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e8a701123029cc240cea61dd2d16ad57cab4691804143ce80ecd9286b464d180"}, - {file = "pyzmq-25.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:61706a6b6c24bdece85ff177fec393545a3191eeda35b07aaa1458a027ad1304"}, - {file = "pyzmq-25.1.1.tar.gz", hash = "sha256:259c22485b71abacdfa8bf79720cd7bcf4b9d128b30ea554f01ae71fdbfdaa23"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:28b119ba97129d3001673a697b7cce47fe6de1f7255d104c2f01108a5179a066"}, + {file = "pyzmq-24.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bcbebd369493d68162cddb74a9c1fcebd139dfbb7ddb23d8f8e43e6c87bac3a6"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae61446166983c663cee42c852ed63899e43e484abf080089f771df4b9d272ef"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87f7ac99b15270db8d53f28c3c7b968612993a90a5cf359da354efe96f5372b4"}, + {file = "pyzmq-24.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9dca7c3956b03b7663fac4d150f5e6d4f6f38b2462c1e9afd83bcf7019f17913"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8c78bfe20d4c890cb5580a3b9290f700c570e167d4cdcc55feec07030297a5e3"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:48f721f070726cd2a6e44f3c33f8ee4b24188e4b816e6dd8ba542c8c3bb5b246"}, + {file = "pyzmq-24.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afe1f3bc486d0ce40abb0a0c9adb39aed3bbac36ebdc596487b0cceba55c21c1"}, + {file = "pyzmq-24.0.1-cp310-cp310-win32.whl", hash = "sha256:3e6192dbcefaaa52ed81be88525a54a445f4b4fe2fffcae7fe40ebb58bd06bfd"}, + {file = "pyzmq-24.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:86de64468cad9c6d269f32a6390e210ca5ada568c7a55de8e681ca3b897bb340"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:838812c65ed5f7c2bd11f7b098d2e5d01685a3f6d1f82849423b570bae698c00"}, + {file = "pyzmq-24.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dfb992dbcd88d8254471760879d48fb20836d91baa90f181c957122f9592b3dc"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7abddb2bd5489d30ffeb4b93a428130886c171b4d355ccd226e83254fcb6b9ef"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:94010bd61bc168c103a5b3b0f56ed3b616688192db7cd5b1d626e49f28ff51b3"}, + {file = "pyzmq-24.0.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8242543c522d84d033fe79be04cb559b80d7eb98ad81b137ff7e0a9020f00ace"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ccb94342d13e3bf3ffa6e62f95b5e3f0bc6bfa94558cb37f4b3d09d6feb536ff"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6640f83df0ae4ae1104d4c62b77e9ef39be85ebe53f636388707d532bee2b7b8"}, + {file = "pyzmq-24.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a180dbd5ea5d47c2d3b716d5c19cc3fb162d1c8db93b21a1295d69585bfddac1"}, + {file = "pyzmq-24.0.1-cp311-cp311-win32.whl", hash = "sha256:624321120f7e60336be8ec74a172ae7fba5c3ed5bf787cc85f7e9986c9e0ebc2"}, + {file = "pyzmq-24.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:1724117bae69e091309ffb8255412c4651d3f6355560d9af312d547f6c5bc8b8"}, + {file = "pyzmq-24.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:15975747462ec49fdc863af906bab87c43b2491403ab37a6d88410635786b0f4"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b947e264f0e77d30dcbccbb00f49f900b204b922eb0c3a9f0afd61aaa1cedc3d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ec91f1bad66f3ee8c6deb65fa1fe418e8ad803efedd69c35f3b5502f43bd1dc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:db03704b3506455d86ec72c3358a779e9b1d07b61220dfb43702b7b668edcd0d"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e7e66b4e403c2836ac74f26c4b65d8ac0ca1eef41dfcac2d013b7482befaad83"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7a23ccc1083c260fa9685c93e3b170baba45aeed4b524deb3f426b0c40c11639"}, + {file = "pyzmq-24.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fa0ae3275ef706c0309556061185dd0e4c4cd3b7d6f67ae617e4e677c7a41e2e"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win32.whl", hash = "sha256:f01de4ec083daebf210531e2cca3bdb1608dbbbe00a9723e261d92087a1f6ebc"}, + {file = "pyzmq-24.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:de4217b9eb8b541cf2b7fde4401ce9d9a411cc0af85d410f9d6f4333f43640be"}, + {file = "pyzmq-24.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:78068e8678ca023594e4a0ab558905c1033b2d3e806a0ad9e3094e231e115a33"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77c2713faf25a953c69cf0f723d1b7dd83827b0834e6c41e3fb3bbc6765914a1"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bb4af15f305056e95ca1bd086239b9ebc6ad55e9f49076d27d80027f72752f6"}, + {file = "pyzmq-24.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0f14cffd32e9c4c73da66db97853a6aeceaac34acdc0fae9e5bbc9370281864c"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0108358dab8c6b27ff6b985c2af4b12665c1bc659648284153ee501000f5c107"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d66689e840e75221b0b290b0befa86f059fb35e1ee6443bce51516d4d61b6b99"}, + {file = "pyzmq-24.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ae08ac90aa8fa14caafc7a6251bd218bf6dac518b7bff09caaa5e781119ba3f2"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win32.whl", hash = "sha256:8421aa8c9b45ea608c205db9e1c0c855c7e54d0e9c2c2f337ce024f6843cab3b"}, + {file = "pyzmq-24.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54d8b9c5e288362ec8595c1d98666d36f2070fd0c2f76e2b3c60fbad9bd76227"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:acbd0a6d61cc954b9f535daaa9ec26b0a60a0d4353c5f7c1438ebc88a359a47e"}, + {file = "pyzmq-24.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:47b11a729d61a47df56346283a4a800fa379ae6a85870d5a2e1e4956c828eedc"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abe6eb10122f0d746a0d510c2039ae8edb27bc9af29f6d1b05a66cc2401353ff"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:07bec1a1b22dacf718f2c0e71b49600bb6a31a88f06527dfd0b5aababe3fa3f7"}, + {file = "pyzmq-24.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0d945a85b70da97ae86113faf9f1b9294efe66bd4a5d6f82f2676d567338b66"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1b7928bb7580736ffac5baf814097be342ba08d3cfdfb48e52773ec959572287"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b946da90dc2799bcafa682692c1d2139b2a96ec3c24fa9fc6f5b0da782675330"}, + {file = "pyzmq-24.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c8840f064b1fb377cffd3efeaad2b190c14d4c8da02316dae07571252d20b31f"}, + {file = "pyzmq-24.0.1-cp38-cp38-win32.whl", hash = "sha256:4854f9edc5208f63f0841c0c667260ae8d6846cfa233c479e29fdc85d42ebd58"}, + {file = "pyzmq-24.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:42d4f97b9795a7aafa152a36fe2ad44549b83a743fd3e77011136def512e6c2a"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:52afb0ac962963fff30cf1be775bc51ae083ef4c1e354266ab20e5382057dd62"}, + {file = "pyzmq-24.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bad8210ad4df68c44ff3685cca3cda448ee46e20d13edcff8909eba6ec01ca4"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dabf1a05318d95b1537fd61d9330ef4313ea1216eea128a17615038859da3b3b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5bd3d7dfd9cd058eb68d9a905dec854f86649f64d4ddf21f3ec289341386c44b"}, + {file = "pyzmq-24.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8012bce6836d3f20a6c9599f81dfa945f433dab4dbd0c4917a6fb1f998ab33d"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c31805d2c8ade9b11feca4674eee2b9cce1fec3e8ddb7bbdd961a09dc76a80ea"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3104f4b084ad5d9c0cb87445cc8cfd96bba710bef4a66c2674910127044df209"}, + {file = "pyzmq-24.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:df0841f94928f8af9c7a1f0aaaffba1fb74607af023a152f59379c01c53aee58"}, + {file = "pyzmq-24.0.1-cp39-cp39-win32.whl", hash = "sha256:a435ef8a3bd95c8a2d316d6e0ff70d0db524f6037411652803e118871d703333"}, + {file = "pyzmq-24.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:2032d9cb994ce3b4cba2b8dfae08c7e25bc14ba484c770d4d3be33c27de8c45b"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bb5635c851eef3a7a54becde6da99485eecf7d068bd885ac8e6d173c4ecd68b0"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:83ea1a398f192957cb986d9206ce229efe0ee75e3c6635baff53ddf39bd718d5"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:941fab0073f0a54dc33d1a0460cb04e0d85893cb0c5e1476c785000f8b359409"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e8f482c44ccb5884bf3f638f29bea0f8dc68c97e38b2061769c4cb697f6140d"}, + {file = "pyzmq-24.0.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:613010b5d17906c4367609e6f52e9a2595e35d5cc27d36ff3f1b6fa6e954d944"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:65c94410b5a8355cfcf12fd600a313efee46ce96a09e911ea92cf2acf6708804"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:20e7eeb1166087db636c06cae04a1ef59298627f56fb17da10528ab52a14c87f"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2712aee7b3834ace51738c15d9ee152cc5a98dc7d57dd93300461b792ab7b43"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a7c280185c4da99e0cc06c63bdf91f5b0b71deb70d8717f0ab870a43e376db8"}, + {file = "pyzmq-24.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:858375573c9225cc8e5b49bfac846a77b696b8d5e815711b8d4ba3141e6e8879"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:80093b595921eed1a2cead546a683b9e2ae7f4a4592bb2ab22f70d30174f003a"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f3f3154fde2b1ff3aa7b4f9326347ebc89c8ef425ca1db8f665175e6d3bd42f"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb756147314430bee5d10919b8493c0ccb109ddb7f5dfd2fcd7441266a25b75"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e706bac34e9f50779cb8c39f10b53a4d15aebb97235643d3112ac20bd577b4"}, + {file = "pyzmq-24.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:687700f8371643916a1d2c61f3fdaa630407dd205c38afff936545d7b7466066"}, + {file = "pyzmq-24.0.1.tar.gz", hash = "sha256:216f5d7dbb67166759e59b0479bca82b8acf9bed6015b526b8eb10143fb08e77"}, ] [package.dependencies] cffi = {version = "*", markers = "implementation_name == \"pypy\""} +py = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "recommonmark" version = "0.7.1" description = "A docutils-compatibility bridge to CommonMark, enabling you to write CommonMark inside of Docutils & Sphinx projects." +category = "dev" optional = false python-versions = "*" files = [ @@ -3818,6 +3929,7 @@ sphinx = ">=1.3.1" name = "referencing" version = "0.30.2" description = "JSON Referencing + Python" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -3833,6 +3945,7 @@ rpds-py = ">=0.7.0" name = "requests" version = "2.31.0" description = "Python HTTP for Humans." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -3854,6 +3967,7 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "restructuredtext-lint" version = "1.4.0" description = "reStructuredText linter" +category = "dev" optional = false python-versions = "*" files = [ @@ -3867,6 +3981,7 @@ docutils = ">=0.11,<1.0" name = "rfc3339-validator" version = "0.1.4" description = "A pure python RFC3339 validator" +category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3881,6 +3996,7 @@ six = "*" name = "rfc3986-validator" version = "0.1.1" description = "Pure python rfc3986 validator" +category = "main" optional = true python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -3892,6 +4008,7 @@ files = [ name = "rich" version = "13.6.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" optional = false python-versions = ">=3.7.0" files = [ @@ -3911,6 +4028,7 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "rpds-py" version = "0.10.6" description = "Python bindings to Rust's persistent data structures (rpds)" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4019,6 +4137,7 @@ files = [ name = "scipy" version = "1.10.1" description = "Fundamental algorithms for scientific computing in Python" +category = "main" optional = false python-versions = "<3.12,>=3.8" files = [ @@ -4057,6 +4176,7 @@ test = ["asv", "gmpy2", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeo name = "screeninfo" version = "0.8.1" description = "Fetch location and size of physical screens." +category = "main" optional = false python-versions = ">=3.6.2,<4.0.0" files = [ @@ -4072,6 +4192,7 @@ pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""} name = "send2trash" version = "1.8.2" description = "Send file to trash natively under Mac OS X, Windows and Linux" +category = "main" optional = true python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -4088,6 +4209,7 @@ win32 = ["pywin32"] name = "setuptools" version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4104,6 +4226,7 @@ testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jar name = "setuptools-scm" version = "8.0.4" description = "the blessed package to manage your versions by scm tags" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4126,6 +4249,7 @@ test = ["build", "pytest", "rich", "wheel"] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -4137,6 +4261,7 @@ files = [ name = "skia-pathops" version = "0.7.4" description = "Python access to operations on paths using the Skia library" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4192,6 +4317,7 @@ testing = ["coverage", "pytest", "pytest-randomly", "pytest-xdist"] name = "smmap" version = "5.0.1" description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4203,6 +4329,7 @@ files = [ name = "sniffio" version = "1.3.0" description = "Sniff out which async library your code is running under" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4214,6 +4341,7 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" optional = false python-versions = "*" files = [ @@ -4225,6 +4353,7 @@ files = [ name = "soupsieve" version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4236,6 +4365,7 @@ files = [ name = "sphinx" version = "4.5.0" description = "Python documentation generator" +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -4271,6 +4401,7 @@ test = ["cython", "html5lib", "pytest", "pytest-cov", "typed-ast"] name = "sphinx-basic-ng" version = "1.0.0b2" description = "A modern skeleton for Sphinx themes." +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4288,6 +4419,7 @@ docs = ["furo", "ipython", "myst-parser", "sphinx-copybutton", "sphinx-inline-ta name = "sphinx-copybutton" version = "0.4.0" description = "Add a copy button to each of your code cells." +category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -4306,6 +4438,7 @@ rtd = ["ipython", "sphinx", "sphinx-book-theme"] name = "sphinxcontrib-applehelp" version = "1.0.4" description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4321,6 +4454,7 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4336,6 +4470,7 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" optional = false python-versions = ">=3.8" files = [ @@ -4351,6 +4486,7 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4365,6 +4501,7 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-programoutput" version = "0.17" description = "Sphinx extension to include program output" +category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" files = [ @@ -4379,6 +4516,7 @@ Sphinx = ">=1.7.0" name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4394,6 +4532,7 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -4409,6 +4548,7 @@ test = ["pytest"] name = "sphinxext-opengraph" version = "0.8.2" description = "Sphinx Extension to enable OGP support" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4424,6 +4564,7 @@ sphinx = ">=4.0" name = "srt" version = "3.5.3" description = "A tiny library for parsing, modifying, and composing SRT files." +category = "main" optional = false python-versions = ">=2.7" files = [ @@ -4434,6 +4575,7 @@ files = [ name = "stack-data" version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" +category = "main" optional = true python-versions = "*" files = [ @@ -4453,6 +4595,7 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] name = "svgelements" version = "1.9.6" description = "Svg Elements Parsing" +category = "main" optional = false python-versions = "*" files = [ @@ -4464,6 +4607,7 @@ files = [ name = "terminado" version = "0.17.1" description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4484,6 +4628,7 @@ test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] name = "tinycss2" version = "1.2.1" description = "A tiny CSS parser" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4502,6 +4647,7 @@ test = ["flake8", "isort", "pytest"] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4513,6 +4659,7 @@ files = [ name = "tomlkit" version = "0.12.2" description = "Style preserving TOML library" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4524,6 +4671,7 @@ files = [ name = "tornado" version = "6.3.3" description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." +category = "main" optional = true python-versions = ">= 3.8" files = [ @@ -4544,6 +4692,7 @@ files = [ name = "tqdm" version = "4.66.1" description = "Fast, Extensible Progress Meter" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4564,6 +4713,7 @@ telegram = ["requests"] name = "traitlets" version = "5.13.0" description = "Traitlets Python configuration system" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4579,6 +4729,7 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.6.0)", "pre-commit", "pytest (>=7.0, name = "types-decorator" version = "0.1.7" description = "Typing stubs for decorator" +category = "dev" optional = false python-versions = "*" files = [ @@ -4590,6 +4741,7 @@ files = [ name = "types-docutils" version = "0.20.0.3" description = "Typing stubs for docutils" +category = "dev" optional = false python-versions = "*" files = [ @@ -4599,19 +4751,21 @@ files = [ [[package]] name = "types-pillow" -version = "8.3.11" +version = "9.5.0.6" description = "Typing stubs for Pillow" +category = "dev" optional = false python-versions = "*" files = [ - {file = "types-Pillow-8.3.11.tar.gz", hash = "sha256:aa96a739184f48f69e6f30218400623fc5a95f5fec199c447663a32538440405"}, - {file = "types_Pillow-8.3.11-py3-none-any.whl", hash = "sha256:998189334e616b1dd42c9634669efbf726184039e96e9a23ec95246e0ecff3fc"}, + {file = "types-Pillow-9.5.0.6.tar.gz", hash = "sha256:6a0cad40686e5c35fe7ada70f52bd3970395d31ece33486609e5420e820a9e4e"}, + {file = "types_Pillow-9.5.0.6-py3-none-any.whl", hash = "sha256:1d238abaa9d529b04941d805b7f4d3f7df30702bb14521ec507617f117406fb4"}, ] [[package]] name = "types-protobuf" version = "3.20.4.6" description = "Typing stubs for protobuf" +category = "dev" optional = false python-versions = "*" files = [ @@ -4623,6 +4777,7 @@ files = [ name = "types-pygments" version = "2.16.0.0" description = "Typing stubs for Pygments" +category = "dev" optional = false python-versions = "*" files = [ @@ -4638,6 +4793,7 @@ types-setuptools = "*" name = "types-python-dateutil" version = "2.8.19.14" description = "Typing stubs for python-dateutil" +category = "main" optional = true python-versions = "*" files = [ @@ -4649,6 +4805,7 @@ files = [ name = "types-requests" version = "2.31.0.10" description = "Typing stubs for requests" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4663,6 +4820,7 @@ urllib3 = ">=2" name = "types-setuptools" version = "57.4.18" description = "Typing stubs for setuptools" +category = "dev" optional = false python-versions = "*" files = [ @@ -4674,6 +4832,7 @@ files = [ name = "typing-extensions" version = "4.8.0" description = "Backported and Experimental Type Hints for Python 3.8+" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -4685,6 +4844,7 @@ files = [ name = "uri-template" version = "1.3.0" description = "RFC 6570 URI Template Processor" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4699,6 +4859,7 @@ dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake name = "urllib3" version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4716,6 +4877,7 @@ zstd = ["zstandard (>=0.18.0)"] name = "virtualenv" version = "20.24.6" description = "Virtual Python Environment builder" +category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -4736,6 +4898,7 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess name = "watchdog" version = "3.0.0" description = "Filesystem events monitoring" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -4775,6 +4938,7 @@ watchmedo = ["PyYAML (>=3.10)"] name = "wcwidth" version = "0.2.9" description = "Measures the displayed width of unicode strings in a terminal" +category = "main" optional = true python-versions = "*" files = [ @@ -4786,6 +4950,7 @@ files = [ name = "webcolors" version = "1.13" description = "A library for working with the color formats defined by HTML and CSS." +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -4801,6 +4966,7 @@ tests = ["pytest", "pytest-cov"] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" +category = "main" optional = true python-versions = "*" files = [ @@ -4812,6 +4978,7 @@ files = [ name = "websocket-client" version = "1.6.4" description = "WebSocket client for Python with low level API options" +category = "main" optional = true python-versions = ">=3.8" files = [ @@ -4828,6 +4995,7 @@ test = ["websockets"] name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." +category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ @@ -4912,6 +5080,7 @@ files = [ name = "y-py" version = "0.6.2" description = "Python bindings for the Y-CRDT built from yrs (Rust)" +category = "main" optional = true python-versions = "*" files = [ @@ -4995,6 +5164,7 @@ files = [ name = "ypy-websocket" version = "0.8.4" description = "WebSocket connector for Ypy" +category = "main" optional = true python-versions = ">=3.7" files = [ @@ -5014,6 +5184,7 @@ test = ["mypy", "pre-commit", "pytest", "pytest-asyncio", "websockets (>=10.0)"] name = "zipp" version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -5032,4 +5203,4 @@ jupyterlab = ["jupyterlab", "notebook"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "fcf22a432b1736dc21eeefb88afc680020c64357eb7f8b1ba4b4fa7d030debaa" +content-hash = "aeec95f223cfa390c61f8ffa470c01d9417c286022443c3560e5e30b467b7060" diff --git a/pyproject.toml b/pyproject.toml index 7b9f1a824f..57eb56b605 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,7 +29,7 @@ packages = [ python = ">=3.8,<3.12" click = ">=7.2,<=9.0" click-default-group = "^1.2.2" -numpy = "^1.19" +numpy = ">=1.22" Pillow = ">=9.1,<10.0" scipy = "^1.7.3" tqdm = "^4.62.3" @@ -78,12 +78,11 @@ pygithub = "^1" flake8 = "^3.9.0" isort = "^5.8.0" pytest-xdist = "^2.2" -mypy = "^0.931" types-requests = "^2.25.6" types-protobuf = "^3.17.4" types-decorator = "^0.1.7" types-setuptools = "^57.0.2" -types-Pillow = "^8.3.3" +types-Pillow = "^9.3.0.4" types-Pygments = "^2.9.2" flake8-builtins = "^1.5.3" flake8-bugbear = "^21.4.3"