mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Fix Typing (#3086)
* first draft of color class + starting library conversion * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * changed everything to Manim color todo: figure out circular dependency in utils * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * first working draft of new color version * resolving conflicts * resolving conflicts * resolving conflicts * resolving conflicts * resolving conflicts * changed default internal value of ManimColor to np.ndarray[float] * starting to fix tests * fixed more tests and changed precision of manim color * removed premature color conversion * fixed some more tests * final test changes * fix doctests * fix for 3.8 * fixing ManimColor string representation * removing some unneccesary conversions * moved community constants to manim_colors.py and added more color standards * Added typing.py and typed bezier.py, core.py, constants.py fully * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fixed codeql complaints * add type ignore for np.allclose * fixed import in three_dimensions * added ignore for F401 back again in flake * added typings to coordinate_systems.py * Few improvements to `graphing/coordinate_systems.py` * added some typings to mobject/geometry/line.py * updated typings for mobject/geometry/line.py * Add missing imports to `line.py` * added typings to three_dimensions.py * Use `FunctionOverride` for animation overrides Fix type signature of `set_color_by_gradient` * Remove `TYPE_CHECKING` check Doc is failing * Revert "Remove `TYPE_CHECKING` check" Fails due to circular import * Use `Self` in `coordinate_systems.py` * Typehinted mobject.py and updated manim.typing.py * Typed VMobject * Type-hinted manim.mobject.geometry * math.cos->np.cos, etc & fixed incorrect typehints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix missing annotations import * TypeAlias fix in typing.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add ignore errors again to mypy because commits are not possible like this * Fix last typing issues * Update docs * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Only type check manim * Try fixing pre-commit * fix merge * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix compat * Fix compat again * Fix imports compat * Use union syntax * Use union syntax * Fix reduce_across_dimension * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Various test and merge fixes * Doc fixes * Last doc fix * Revert usage of np over math * Bump numpy version * Remove obsolete duplicate example * Fixed Incorrect Typehint in manim.constants * Fix docstring typo * More fixes Use mypy.ini instead of .mypy.ini Fix more docstrings Improve types in utils and constants * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * docs fixes * Add internal aliases * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix compat * line lengths in .rst file, formatting, typos * add docstring for space_ops:cross2d * add some more arrow tip typings (in a non-circular import causing way) * yes, this can be deleted * fix formatting of example * added docstring to bezier::inverse_interpolation * added docstring + test for bezier::match_interpolate * some improvements in coordinate_systems * Vector -> Vector3 * replaced np.ndarray with more appropriate type hints * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply feedback * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert to previous (new) version * fix doctest * fix ReST errors --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alex Lembcke <alex.lembcke@gmail.com> Co-authored-by: Viicos <65306057+Viicos@users.noreply.github.com> Co-authored-by: JasonGrace2282 <aarush.deshpande@gmail.com> Co-authored-by: Benjamin Hackl <devel@benjamin-hackl.at>
This commit is contained in:
parent
25bb381528
commit
d77a47a233
34 changed files with 2156 additions and 1347 deletions
7
.flake8
7
.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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,27 +2,62 @@
|
|||
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.
|
||||
|
||||
To be able to use the newest typing features not available in the lowest
|
||||
supported Python version, make use of `typing_extensions`_.
|
||||
|
||||
To be able to use the new Union syntax (``|``) and builtins subscripting, use
|
||||
the ``from __future__ import annotations`` import.
|
||||
|
||||
.. _mypy: https://mypy-lang.org/
|
||||
.. _typing_extensions: https://pypi.org/project/typing-extensions/
|
||||
|
||||
Typing guidelines
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
* 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 <https://github.com/numpy/numpy/issues/16544>`_). Besides the
|
||||
pending shape support, using the correct type aliases will help users understand
|
||||
which shape should be used.
|
||||
|
||||
* 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 set_points_as_corners(self, points: Sequence[float]) -> "VMobject":
|
||||
"""Given an array of points, set them as corner of the Vmobject."""
|
||||
def height(self, value) -> None:
|
||||
self.scale_to_fit_height(value)
|
||||
|
||||
* ``**kwargs`` has no typehint
|
||||
* For variables representing paths, use the ``StrPath`` or ``StrOrBytesPath``
|
||||
type alias defined in the :mod:`~.typing` module.
|
||||
|
||||
* Mobjects have the typehint "Mobject", e.g.
|
||||
* ``*args`` and ``**kwargs`` shouldn't be left untyped (in most cases you can
|
||||
use ``Any``).
|
||||
|
||||
* Following `PEP 484 <https://peps.python.org/pep-0484/#the-numeric-tower>`_,
|
||||
use ``float`` instead of ``int | float``.
|
||||
|
||||
* Mobjects have the typehint ``Mobject``, e.g.:
|
||||
|
||||
.. code:: py
|
||||
|
||||
|
|
@ -30,74 +65,17 @@ When adding type hints to manim, there are some guidelines that should be follow
|
|||
"""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. )
|
||||
|
||||
.. 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
|
||||
|
||||
def rotate(
|
||||
self,
|
||||
angle,
|
||||
axis=OUT,
|
||||
about_point: Optional[Sequence[float]] = None,
|
||||
**kwargs,
|
||||
):
|
||||
pass
|
||||
|
||||
|
||||
* The ``__init__()`` method always should have None as its return type.
|
||||
|
||||
* Functions and lambda functions should get the typehint ``Callable``
|
||||
* Always parametrize generics (``list[int]`` instead of ``list``,
|
||||
``type[Any]`` instead of ``type``, etc.). This also applies to callables:
|
||||
|
||||
.. 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]``
|
||||
|
||||
.. note::
|
||||
As a helper for tool for typesets, you can use `typestring-parser
|
||||
<https://github.com/Dominik1123/typestring-parser>`_
|
||||
which can be accessed by first installing it via ``pip`` - ``pip install typestring-parser`` and
|
||||
then using ``from typestring_parser import parse``.
|
||||
|
||||
.. doctest::
|
||||
:options: +SKIP
|
||||
|
||||
>>> from typestring_parser import parse
|
||||
>>> parse("int")
|
||||
<class '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]]
|
||||
|
||||
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``...)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ __all__ = [
|
|||
]
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ from rich.theme import Theme
|
|||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
HIGHLIGHTED_KEYWORDS = [ # these keywords are highlighted specially
|
||||
"Played",
|
||||
"animations",
|
||||
|
|
|
|||
|
|
@ -27,9 +27,9 @@ import numpy as np
|
|||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ __all__ = ["AnimatedBoundary", "TracedPath"]
|
|||
|
||||
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 (
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ from ..constants import TAU
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ from manim.mobject.geometry.arc import Circle, Dot
|
|||
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 @@ class ShowPassingFlash(ShowPartial):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import copy
|
|||
import itertools as it
|
||||
import operator as op
|
||||
import pathlib
|
||||
import time
|
||||
from functools import reduce
|
||||
from typing import Any, Callable, Iterable
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import numpy as np
|
|||
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 @@ __all__ = [
|
|||
]
|
||||
# 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 @@ RESAMPLING_ALGORITHMS = {
|
|||
}
|
||||
|
||||
# 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 @@ QUALITIES: dict[str, dict[str, str | int | None]] = {
|
|||
},
|
||||
}
|
||||
|
||||
DEFAULT_QUALITY: str = "high_quality"
|
||||
DEFAULT_QUALITY = "high_quality"
|
||||
|
||||
EPILOG = "Made with <3 by Manim Community developers."
|
||||
SHIFT_VALUE = 65505
|
||||
|
|
|
|||
|
|
@ -43,16 +43,16 @@ __all__ = [
|
|||
]
|
||||
|
||||
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 @@ from manim.utils.space_ops import (
|
|||
)
|
||||
|
||||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
# 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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Arc(TipableVMobject):
|
|||
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 @@ class Arc(TipableVMobject):
|
|||
# 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 @@ class Arc(TipableVMobject):
|
|||
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 @@ class Arc(TipableVMobject):
|
|||
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 @@ class Arc(TipableVMobject):
|
|||
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 @@ class Arc(TipableVMobject):
|
|||
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 @@ class ArcBetweenPoints(Arc):
|
|||
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 @@ class ArcBetweenPoints(Arc):
|
|||
"""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 @@ class ArcBetweenPoints(Arc):
|
|||
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 @@ class CurvedArrow(ArcBetweenPoints):
|
|||
|
||||
|
||||
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 @@ class Circle(Arc):
|
|||
radius: float | None = None,
|
||||
color: ParsableManimColor = RED,
|
||||
**kwargs,
|
||||
):
|
||||
) -> None:
|
||||
super().__init__(
|
||||
radius=radius,
|
||||
start_angle=0,
|
||||
|
|
@ -508,7 +533,7 @@ class Circle(Arc):
|
|||
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 @@ class Circle(Arc):
|
|||
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 @@ class Circle(Arc):
|
|||
|
||||
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 @@ class Dot(Circle):
|
|||
|
||||
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 @@ class Ellipse(Circle):
|
|||
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 @@ class AnnularSector(Arc):
|
|||
|
||||
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 @@ class AnnularSector(Arc):
|
|||
**kwargs,
|
||||
)
|
||||
|
||||
def generate_points(self):
|
||||
def generate_points(self) -> None:
|
||||
inner_arc, outer_arc = (
|
||||
Arc(
|
||||
start_angle=self.start_angle,
|
||||
|
|
@ -879,7 +902,9 @@ class Sector(AnnularSector):
|
|||
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 @@ class Annulus(Circle):
|
|||
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 @@ class Annulus(Circle):
|
|||
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 @@ class CubicBezier(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
"""
|
||||
|
||||
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 @@ class ArcPolygon(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
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 @@ class ArcPolygonFromArcs(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArcPolygonFromArcs(VMobject, metaclass=ConvertToOpenGL):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -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 pathops import PathVerb, difference, intersection, union, xor
|
|||
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 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
Returns
|
||||
-------
|
||||
typing.List[np.ndarray]
|
||||
Point2D_Array
|
||||
A list of array converted to 3d.
|
||||
|
||||
Example
|
||||
|
|
@ -216,7 +212,7 @@ class Difference(_BooleanOps):
|
|||
|
||||
"""
|
||||
|
||||
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 @@ class Intersection(_BooleanOps):
|
|||
|
||||
"""
|
||||
|
||||
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 @@ class Exclusion(_BooleanOps):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, subject, clip, **kwargs) -> None:
|
||||
def __init__(self, subject: VMobject, clip: VMobject, **kwargs) -> None:
|
||||
super().__init__(**kwargs)
|
||||
outpen = SkiaPath()
|
||||
xor(
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ __all__ = [
|
|||
"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.mobject import Mobject
|
|||
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 @@ class Line(TipableVMobject):
|
|||
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 @@ class Line(TipableVMobject):
|
|||
|
||||
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 @@ class Line(TipableVMobject):
|
|||
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 @@ class Line(TipableVMobject):
|
|||
|
||||
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 @@ class Line(TipableVMobject):
|
|||
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 @@ class Line(TipableVMobject):
|
|||
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 @@ class Line(TipableVMobject):
|
|||
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 @@ class Line(TipableVMobject):
|
|||
|
||||
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 @@ class DashedLine(Line):
|
|||
|
||||
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 @@ class DashedLine(Line):
|
|||
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 @@ class DashedLine(Line):
|
|||
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 @@ class DashedLine(Line):
|
|||
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 @@ class DashedLine(Line):
|
|||
|
||||
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 @@ class TangentLine(Line):
|
|||
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 @@ class Elbow(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Arrow(Line):
|
|||
|
||||
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 @@ class Arrow(Line):
|
|||
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 @@ class Arrow(Line):
|
|||
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 @@ class Arrow(Line):
|
|||
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 @@ class Arrow(Line):
|
|||
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 @@ class Vector(Arrow):
|
|||
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 @@ class Vector(Arrow):
|
|||
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 @@ class DoubleArrow(Arrow):
|
|||
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 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
|
|||
dot_color: ParsableManimColor = WHITE,
|
||||
elbow: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self.lines = (line1, line2)
|
||||
self.quadrant = quadrant
|
||||
|
|
@ -1017,14 +1037,10 @@ class Angle(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class RightAngle(Angle):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ __all__ = [
|
|||
"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.constants import *
|
|||
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 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
|
|||
[*(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 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
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 @@ class Polygram(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Polygon(Polygram):
|
|||
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 @@ class RegularPolygram(Polygram):
|
|||
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 @@ class RegularPolygon(RegularPolygram):
|
|||
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 @@ class Star(Polygon):
|
|||
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 @@ class Triangle(RegularPolygon):
|
|||
self.add(tri_group)
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs) -> None:
|
||||
super().__init__(n=3, **kwargs)
|
||||
|
||||
|
||||
|
|
@ -664,7 +673,7 @@ class Square(Rectangle):
|
|||
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 @@ class Cutout(VMobject, metaclass=ConvertToOpenGL):
|
|||
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":
|
||||
|
|
|
|||
|
|
@ -4,13 +4,17 @@ from __future__ import annotations
|
|||
|
||||
__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 @@ class SurroundingRectangle(RoundedRectangle):
|
|||
"""
|
||||
|
||||
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 @@ class BackgroundRectangle(SurroundingRectangle):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
mobject,
|
||||
mobject: Mobject,
|
||||
color: ParsableManimColor | None = None,
|
||||
stroke_width: float = 0,
|
||||
stroke_opacity: float = 0,
|
||||
|
|
@ -98,13 +107,13 @@ class BackgroundRectangle(SurroundingRectangle):
|
|||
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 @@ class BackgroundRectangle(SurroundingRectangle):
|
|||
)
|
||||
return self
|
||||
|
||||
def get_fill_color(self):
|
||||
def get_fill_color(self) -> ManimColor:
|
||||
return self.color
|
||||
|
||||
|
||||
|
|
@ -153,10 +162,10 @@ class Cross(VGroup):
|
|||
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 @@ class Underline(Line):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ __all__ = [
|
|||
"StealthTip",
|
||||
]
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
|
||||
from manim.constants import *
|
||||
|
|
@ -22,6 +24,9 @@ from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
|
|||
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 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ArrowCircleTip(ArrowTip, Circle):
|
|||
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 @@ class ArrowSquareTip(ArrowTip, Square):
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,10 @@ __all__ = [
|
|||
|
||||
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 @@ from manim.utils.space_ops import angle_of_vector
|
|||
|
||||
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 @@ class CoordinateSystem:
|
|||
|
||||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
"""
|
||||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
"""
|
||||
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 @@ class CoordinateSystem:
|
|||
direction: Sequence[float] = UP * 0.5 + RIGHT,
|
||||
buff: float = SMALL_BUFF,
|
||||
**kwargs,
|
||||
):
|
||||
) -> Mobject:
|
||||
"""Generate a y-axis label.
|
||||
|
||||
Parameters
|
||||
|
|
@ -370,9 +376,9 @@ class CoordinateSystem:
|
|||
|
||||
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 @@ class CoordinateSystem:
|
|||
|
||||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
|
||||
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 @@ class CoordinateSystem:
|
|||
self,
|
||||
function: Callable[[float], np.ndarray],
|
||||
use_vectorized: bool = False,
|
||||
**kwargs,
|
||||
**kwargs: Any,
|
||||
) -> ParametricFunction:
|
||||
"""A parametric curve.
|
||||
|
||||
|
|
@ -784,8 +814,8 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
| 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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
"""
|
||||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
y_intercept: float = 0,
|
||||
samples: int = 50,
|
||||
use_vectorized: bool = False,
|
||||
**kwargs,
|
||||
):
|
||||
**kwargs: Any,
|
||||
) -> ParametricFunction:
|
||||
"""Plots an antiderivative graph.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1619,7 +1654,7 @@ class CoordinateSystem:
|
|||
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 @@ class CoordinateSystem:
|
|||
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 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
@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 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Axes(VGroup, CoordinateSystem, metaclass=ConvertToOpenGL):
|
|||
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 @@ class ThreeDAxes(Axes):
|
|||
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 @@ class ThreeDAxes(Axes):
|
|||
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 @@ class ThreeDAxes(Axes):
|
|||
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 @@ class ThreeDAxes(Axes):
|
|||
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 @@ class NumberPlane(Axes):
|
|||
),
|
||||
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 @@ class NumberPlane(Axes):
|
|||
|
||||
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 @@ class NumberPlane(Axes):
|
|||
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 @@ class PolarPlane(Axes):
|
|||
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 @@ class PolarPlane(Axes):
|
|||
|
||||
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 @@ class PolarPlane(Axes):
|
|||
"""
|
||||
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 @@ class PolarPlane(Axes):
|
|||
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 @@ class PolarPlane(Axes):
|
|||
self,
|
||||
r_values: Iterable[float] | None = None,
|
||||
a_values: Iterable[float] | None = None,
|
||||
):
|
||||
) -> Self:
|
||||
"""Adds the coordinates.
|
||||
|
||||
Parameters
|
||||
|
|
@ -3189,7 +3224,7 @@ class PolarPlane(Axes):
|
|||
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 @@ class ComplexPlane(NumberPlane):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(
|
||||
**kwargs,
|
||||
)
|
||||
|
|
@ -3284,7 +3319,7 @@ class ComplexPlane(NumberPlane):
|
|||
"""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 @@ class ComplexPlane(NumberPlane):
|
|||
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 @@ class ComplexPlane(NumberPlane):
|
|||
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 @@ class ComplexPlane(NumberPlane):
|
|||
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
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -13,8 +13,16 @@ import numpy as np
|
|||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||
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 @@ from manim.utils.bezier import (
|
|||
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 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
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 @@ class OpenGLVMobject(OpenGLMobject):
|
|||
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):
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import operator as op
|
|||
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 *
|
||||
|
|
|
|||
|
|
@ -462,7 +462,7 @@ class Text(SVGMobject):
|
|||
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 @@ class Text(SVGMobject):
|
|||
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 @@ class Text(SVGMobject):
|
|||
# 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 @@ class MarkupText(SVGMobject):
|
|||
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 @@ class MarkupText(SVGMobject):
|
|||
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
|
||||
|
|
|
|||
|
|
@ -14,40 +14,45 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
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))
|
||||
|
|
|
|||
|
|
@ -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 @@ __all__ = [
|
|||
"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 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
|
|||
|
||||
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 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Surface(VGroup, metaclass=ConvertToOpenGL):
|
|||
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 @@ class Sphere(Surface):
|
|||
|
||||
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 @@ class Dot3D(Sphere):
|
|||
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 @@ class Prism(Cube):
|
|||
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 @@ class Cone(Surface):
|
|||
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 @@ class Cone(Surface):
|
|||
],
|
||||
)
|
||||
|
||||
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 @@ class Cylinder(Surface):
|
|||
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 @@ class Line3D(Cylinder):
|
|||
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 @@ class Line3D(Cylinder):
|
|||
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 @@ class Line3D(Cylinder):
|
|||
|
||||
@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 @@ class Line3D(Cylinder):
|
|||
|
||||
@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 @@ class Torus(Surface):
|
|||
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:
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
133
manim/typing.py
Normal file
133
manim/typing.py
Normal file
|
|
@ -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]"
|
||||
|
|
@ -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 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
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 @@ from ..utils.space_ops import cross2d, find_intersection
|
|||
|
||||
|
||||
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<b<=1, return an array of the same size,
|
||||
which describes the portion of the original bezier curve on the interval [a, b].
|
||||
|
||||
|
|
@ -90,23 +106,31 @@ def partial_bezier_points(points: np.ndarray, a: float, b: float) -> 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 partial_quadratic_bezier_points(points, a, b):
|
|||
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.
|
||||
This is a variant of interpolate that returns an integer and the residual
|
||||
|
||||
For example, if start=0, end=10, alpha=0.46, This
|
||||
would return (4, 0.6).
|
||||
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,
|
||||
) -> 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,
|
||||
) -> np.ndarray:
|
||||
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 get_smooth_cubic_bezier_handle_points(points):
|
|||
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 get_smooth_cubic_bezier_handle_points(points):
|
|||
|
||||
|
||||
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 get_smooth_handle_points(
|
|||
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 get_smooth_handle_points(
|
|||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,43 +3,55 @@ color conversion.
|
|||
|
||||
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:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from manim import RED, GREEN, BLUE
|
||||
>>> print(RED)
|
||||
#FC6255
|
||||
|
||||
Note this way uses the name of the colors in UPPERCASE.
|
||||
|
||||
.. note::
|
||||
|
||||
The colors of type "C" have an alias equal to the colorname without a letter,
|
||||
e.g. GREEN = GREEN_C
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# logger = _config.logger
|
||||
import colorsys
|
||||
|
||||
# logger = _config.logger
|
||||
import random
|
||||
from typing import Any, Sequence, Union
|
||||
import re
|
||||
from typing import Any, Sequence, TypeVar, Union, overload
|
||||
|
||||
import numpy as np
|
||||
from typing_extensions import Literal, TypeAlias
|
||||
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,
|
||||
)
|
||||
|
||||
from ...utils.space_ops import normalize
|
||||
|
||||
ManimColorDType: TypeAlias = np.float64
|
||||
ManimFloat: TypeAlias = np.float64
|
||||
ManimInt: TypeAlias = np.int64
|
||||
# import manim._config as _config
|
||||
|
||||
RGB_Array_Float: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimFloat]]"
|
||||
RGB_Tuple_Float: TypeAlias = "tuple[float, float, float]"
|
||||
|
||||
RGB_Array_Int: TypeAlias = "np.ndarray[Literal[3], np.dtype[ManimInt]]"
|
||||
RGB_Tuple_Int: TypeAlias = "tuple[int, int, int]"
|
||||
|
||||
RGBA_Array_Float: TypeAlias = "np.ndarray[Literal[4], np.dtype[ManimFloat]]"
|
||||
RGBA_Tuple_Float: TypeAlias = "tuple[float, float, float, float]"
|
||||
|
||||
RGBA_Array_Int: TypeAlias = "np.ndarray[Literal[4], np.dtype[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 = "np.ndarray[Literal[4], np.dtype[ManimColorDType]]"
|
||||
|
||||
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 @@ class ManimColor:
|
|||
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 @@ class ManimColor:
|
|||
@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 @@ class ManimColor:
|
|||
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 @@ class 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 @@ class ManimColor:
|
|||
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 @@ ParsableManimColor: TypeAlias = Union[
|
|||
"""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
|
||||
|
|
|
|||
|
|
@ -125,87 +125,87 @@ from typing import List
|
|||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 @@ __all__ = [
|
|||
|
||||
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
# ignore most files; should be checked once proper types have been implemented
|
||||
[mypy-manim.__main__]
|
||||
# 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
|
||||
|
||||
[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.renderer.*]
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-manim.scene.*]
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-manim.utils.*]
|
||||
ignore_errors = True
|
||||
|
||||
[mypy-manim.utils.color]
|
||||
ignore_errors = False
|
||||
|
||||
[mypy-manim.animation.*]
|
||||
[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
|
||||
|
||||
501
poetry.lock
generated
501
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue