mirror of
https://github.com/ManimCommunity/manim.git
synced 2026-06-22 10:01:47 +00:00
Merge branch 'main' into guide
This commit is contained in:
commit
fbe933596b
80 changed files with 2097 additions and 902 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -60,7 +60,7 @@ jobs:
|
|||
if: runner.os == 'Linux'
|
||||
uses: awalsh128/cache-apt-pkgs-action@latest
|
||||
with:
|
||||
packages: python3-opengl libpango1.0-dev xvfb
|
||||
packages: python3-opengl libpango1.0-dev xvfb freeglut3-dev
|
||||
version: 1.0
|
||||
|
||||
- name: Install Texlive (Linux)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,9 @@
|
|||
|
||||
.. automodule:: {{ fullname }}
|
||||
|
||||
{% block attributes %}
|
||||
{% if attributes %}
|
||||
.. rubric:: Module Attributes
|
||||
|
||||
.. autosummary::
|
||||
{% for item in attributes %}
|
||||
{{ item }}
|
||||
{%- endfor %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{# SEE manim.utils.docbuild.autoaliasattr_directive #}
|
||||
{# FOR INFORMATION ABOUT THE CUSTOM autoaliasattr DIRECTIVE! #}
|
||||
.. autoaliasattr:: {{ fullname }}
|
||||
|
||||
{% block classes %}
|
||||
{% if classes %}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import sys
|
|||
from pathlib import Path
|
||||
|
||||
import manim
|
||||
from manim.utils.docbuild.module_parsing import parse_module_attributes
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
|
|
@ -44,6 +45,7 @@ extensions = [
|
|||
"sphinxext.opengraph",
|
||||
"manim.utils.docbuild.manim_directive",
|
||||
"manim.utils.docbuild.autocolor_directive",
|
||||
"manim.utils.docbuild.autoaliasattr_directive",
|
||||
"sphinx.ext.graphviz",
|
||||
"sphinx.ext.inheritance_diagram",
|
||||
"sphinxcontrib.programoutput",
|
||||
|
|
@ -54,7 +56,14 @@ extensions = [
|
|||
autosummary_generate = True
|
||||
|
||||
# generate documentation from type hints
|
||||
ALIAS_DOCS_DICT = parse_module_attributes()[0]
|
||||
autodoc_typehints = "description"
|
||||
autodoc_type_aliases = {
|
||||
alias_name: f"~manim.{module}.{alias_name}"
|
||||
for module, module_dict in ALIAS_DOCS_DICT.items()
|
||||
for category_dict in module_dict.values()
|
||||
for alias_name in category_dict.keys()
|
||||
}
|
||||
autoclass_content = "both"
|
||||
|
||||
# controls whether functions documented by the autofunction directive
|
||||
|
|
|
|||
|
|
@ -38,14 +38,10 @@ To get an overview of what our community is currently working on, check out
|
|||
Contributing can be confusing, so here are a few guides:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 3
|
||||
|
||||
contributing/development
|
||||
contributing/docstrings
|
||||
contributing/references
|
||||
contributing/examples
|
||||
contributing/typings
|
||||
contributing/admonitions
|
||||
contributing/docs
|
||||
contributing/testing
|
||||
contributing/performance
|
||||
contributing/internationalization
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ Develop your contribution
|
|||
|
||||
Update the docstrings (the text in triple quotation marks) of any functions
|
||||
or classes you change and include them with any new functions you add.
|
||||
See the :doc:`documentation guide <docstrings>` for more information about how we
|
||||
See the :doc:`documentation guide <docs/docstrings>` for more information about how we
|
||||
prefer our code to be documented. The content of the docstrings will be
|
||||
rendered in the :doc:`reference manual <../reference>`.
|
||||
|
||||
|
|
@ -159,6 +159,8 @@ Develop your contribution
|
|||
As far as development on your local machine goes, these are the main steps you
|
||||
should follow.
|
||||
|
||||
.. _polishing-changes-and-submitting-a-pull-request:
|
||||
|
||||
Polishing Changes and Submitting a Pull Request
|
||||
-----------------------------------------------
|
||||
|
||||
|
|
@ -243,7 +245,8 @@ sticks to our coding conventions.
|
|||
a look at the built HTML files to see whether the formatting of the documentation
|
||||
you added looks as you intended. You can build the documentation locally
|
||||
by running ``make html`` from the ``docs`` directory. Make sure you have `Graphviz <https://graphviz.org/>`_
|
||||
installed locally in order to build the inheritance diagrams.
|
||||
installed locally in order to build the inheritance diagrams. See :doc:`docs` for
|
||||
more information.
|
||||
|
||||
Finally, if the pipeline passes and you are satisfied with your changes: wait for
|
||||
feedback and iterate over any requested changes. You will likely be asked to
|
||||
|
|
|
|||
83
docs/source/contributing/docs.rst
Normal file
83
docs/source/contributing/docs.rst
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
====================
|
||||
Adding Documentation
|
||||
====================
|
||||
|
||||
Building the documentation
|
||||
--------------------------
|
||||
|
||||
When you clone the Manim repository from GitHub, you can access the
|
||||
``docs/`` folder which contains the necessary files to build the
|
||||
documentation.
|
||||
|
||||
To build the docs locally, open a CLI, enter the ``docs/`` folder with the
|
||||
``cd`` command and execute the following depending on your OS:
|
||||
|
||||
- Windows: ``./make.bat html``
|
||||
- macOS and Linux: ``make html``
|
||||
|
||||
The first time you build the docs, the process will take several
|
||||
minutes because it needs to generate all the ``.rst`` (reST:
|
||||
reStructured Text) files from scratch by reading and parsing all the
|
||||
Manim content. The process becomes much shorter the next time, as it
|
||||
rebuilds only the parts which have changed.
|
||||
|
||||
|
||||
Sphinx library and extensions
|
||||
-----------------------------
|
||||
|
||||
Manim uses `Sphinx <https://www.sphinx-doc.org>`_ for building the
|
||||
docs. It also makes use of Sphinx extensions such as:
|
||||
|
||||
- `Autodoc: <https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html>`_
|
||||
imports Manim's Python source code, extracts its docstrings and
|
||||
generates documentation from them.
|
||||
- `Autosummary: <https://www.sphinx-doc.org/en/master/usage/extensions/autosummary.html>`_
|
||||
a complement to Autodoc which adds a special directive ``autosummary``,
|
||||
used in Manim to automatically document classes, methods,
|
||||
attributes, functions, module-level variables and exceptions.
|
||||
Autosummary makes use of `Jinja templates <https://jinja.palletsprojects.com/templates/>`_,
|
||||
which Manim defines for autosummarizing classes and modules
|
||||
inside ``docs/source/_templates/``.
|
||||
- `Graphviz extension for Sphinx: <https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html>`_
|
||||
embeds graphs generated by the `Graphviz <https://graphviz.org/>`_
|
||||
module, which must be installed in order to render the
|
||||
inheritance diagrams in the :doc:`/reference`.
|
||||
- `Napoleon: <https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html>`_
|
||||
enables Sphinx to read Google style docstrings and, in
|
||||
particular for Manim, `NumPy style docstrings <https://numpydoc.readthedocs.io/en/latest/format.html>`_
|
||||
- see :doc:`docs/docstrings` for more information.
|
||||
|
||||
|
||||
Sphinx theme
|
||||
------------
|
||||
|
||||
The theme used for this website is `Furo <https://sphinx-themes.org/sample-sites/furo/>`_.
|
||||
|
||||
|
||||
Custom Sphinx directives
|
||||
------------------------
|
||||
|
||||
Manim implements **custom directives** for use with Autodoc and Autosummary, which
|
||||
are defined in :mod:`~.docbuild`:
|
||||
|
||||
.. currentmodule:: manim.utils.docbuild
|
||||
|
||||
.. autosummary::
|
||||
|
||||
:toctree: ../reference
|
||||
autoaliasattr_directive
|
||||
autocolor_directive
|
||||
manim_directive
|
||||
|
||||
|
||||
Index
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
docs/admonitions
|
||||
docs/docstrings
|
||||
docs/examples
|
||||
docs/references
|
||||
docs/typings
|
||||
|
|
@ -9,23 +9,25 @@ Module Index
|
|||
.. autosummary::
|
||||
:toctree: ../reference
|
||||
|
||||
constants
|
||||
~utils.bezier
|
||||
~utils.color
|
||||
~utils.commands
|
||||
~utils.config_ops
|
||||
~utils.deprecation
|
||||
constants
|
||||
~utils.debug
|
||||
~utils.deprecation
|
||||
~utils.docbuild
|
||||
~utils.hashing
|
||||
~utils.ipython_magic
|
||||
~utils.images
|
||||
~utils.ipython_magic
|
||||
~utils.iterables
|
||||
~utils.paths
|
||||
~utils.rate_functions
|
||||
~utils.simple_functions
|
||||
~utils.sounds
|
||||
~utils.space_ops
|
||||
~utils.testing
|
||||
~utils.tex
|
||||
~utils.tex_templates
|
||||
~utils.tex_file_writing
|
||||
~utils.tex_templates
|
||||
typing
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from ._config import *
|
|||
from .utils.commands import *
|
||||
|
||||
# isort: on
|
||||
import numpy as np
|
||||
|
||||
from .animation.animation import *
|
||||
from .animation.changing import *
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import configparser
|
|||
|
||||
from cloup import Context, HelpFormatter, HelpTheme, Style
|
||||
|
||||
__all__ = ["parse_cli_ctx"]
|
||||
|
||||
|
||||
def parse_cli_ctx(parser: configparser.SectionProxy) -> Context:
|
||||
formatter_settings: dict[str, str | int] = {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ from rich.theme import Theme
|
|||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = ["make_logger", "parse_theme", "set_file_logger", "JSONFormatter"]
|
||||
|
||||
HIGHLIGHTED_KEYWORDS = [ # these keywords are highlighted specially
|
||||
"Played",
|
||||
"animations",
|
||||
|
|
|
|||
|
|
@ -20,18 +20,24 @@ import os
|
|||
import re
|
||||
import sys
|
||||
from collections.abc import Mapping, MutableMapping
|
||||
from enum import EnumMeta
|
||||
from pathlib import Path
|
||||
from typing import Any, ClassVar, Iterable, Iterator, NoReturn
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Iterable, Iterator, NoReturn
|
||||
|
||||
import numpy as np
|
||||
from typing_extensions import Self
|
||||
|
||||
from .. import constants
|
||||
from ..constants import RendererType
|
||||
from ..typing import StrPath, Vector3
|
||||
from ..utils.color import ManimColor
|
||||
from ..utils.tex import TexTemplate, TexTemplateFromFile
|
||||
from manim import constants
|
||||
from manim.constants import RendererType
|
||||
from manim.utils.color import ManimColor
|
||||
from manim.utils.tex import TexTemplate, TexTemplateFromFile
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from enum import EnumMeta
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
from manim.typing import StrPath, Vector3D
|
||||
|
||||
__all__ = ["config_file_paths", "make_config_parser", "ManimConfig", "ManimFrame"]
|
||||
|
||||
|
||||
def config_file_paths() -> list[Path]:
|
||||
|
|
@ -1145,22 +1151,22 @@ class ManimConfig(MutableMapping):
|
|||
)
|
||||
|
||||
@property
|
||||
def top(self) -> Vector3:
|
||||
def top(self) -> Vector3D:
|
||||
"""Coordinate at the center top of the frame."""
|
||||
return self.frame_y_radius * constants.UP
|
||||
|
||||
@property
|
||||
def bottom(self) -> Vector3:
|
||||
def bottom(self) -> Vector3D:
|
||||
"""Coordinate at the center bottom of the frame."""
|
||||
return self.frame_y_radius * constants.DOWN
|
||||
|
||||
@property
|
||||
def left_side(self) -> Vector3:
|
||||
def left_side(self) -> Vector3D:
|
||||
"""Coordinate at the middle left of the frame."""
|
||||
return self.frame_x_radius * constants.LEFT
|
||||
|
||||
@property
|
||||
def right_side(self) -> Vector3:
|
||||
def right_side(self) -> Vector3D:
|
||||
"""Coordinate at the middle right of the frame."""
|
||||
return self.frame_x_radius * constants.RIGHT
|
||||
|
||||
|
|
@ -1801,7 +1807,7 @@ class ManimFrame(Mapping):
|
|||
"left_side",
|
||||
"right_side",
|
||||
}
|
||||
_CONSTANTS: ClassVar[dict[str, Vector3]] = {
|
||||
_CONSTANTS: ClassVar[dict[str, Vector3D]] = {
|
||||
"UP": np.array((0.0, 1.0, 0.0)),
|
||||
"DOWN": np.array((0.0, -1.0, 0.0)),
|
||||
"RIGHT": np.array((1.0, 0.0, 0.0)),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ __all__ = ["Animation", "Wait", "override_animation"]
|
|||
from copy import deepcopy
|
||||
from typing import TYPE_CHECKING, Callable, Iterable, Sequence
|
||||
|
||||
from typing_extensions import Self
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.scene.scene import Scene
|
||||
|
||||
|
|
@ -112,7 +114,7 @@ class Animation:
|
|||
*args,
|
||||
use_override=True,
|
||||
**kwargs,
|
||||
):
|
||||
) -> Self:
|
||||
if isinstance(mobject, Mobject) and use_override:
|
||||
func = mobject.animation_override_for(cls)
|
||||
if func is not None:
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from ..animation.composition import AnimationGroup
|
|||
from ..mobject.mobject import Mobject, Updater, _AnimationBuilder
|
||||
from ..scene.scene import Scene
|
||||
|
||||
__all__ = ["ChangeSpeed"]
|
||||
|
||||
|
||||
class ChangeSpeed(Animation):
|
||||
"""Modifies the speed of passed animation.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,17 @@ If left empty, the default colour will be used.[/red]
|
|||
"""
|
||||
RICH_NON_STYLE_ENTRIES: str = ["log.width", "log.height", "log.timestamps"]
|
||||
|
||||
__all__ = [
|
||||
"value_from_string",
|
||||
"value_from_string",
|
||||
"is_valid_style",
|
||||
"replace_keys",
|
||||
"cfg",
|
||||
"write",
|
||||
"show",
|
||||
"export",
|
||||
]
|
||||
|
||||
|
||||
def value_from_string(value: str) -> str | int | bool:
|
||||
"""Extracts the literal of proper datatype from a string.
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from typing import Callable
|
|||
|
||||
from ..._config import config
|
||||
|
||||
__all__ = ["HEALTH_CHECKS"]
|
||||
|
||||
HEALTH_CHECKS = []
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import cloup
|
|||
|
||||
from .checks import HEALTH_CHECKS
|
||||
|
||||
__all__ = ["checkhealth"]
|
||||
|
||||
|
||||
@cloup.command(
|
||||
context_settings=None,
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ CFG_DEFAULTS = {
|
|||
"resolution": (854, 480),
|
||||
}
|
||||
|
||||
__all__ = ["select_resolution", "update_cfg", "project", "scene"]
|
||||
|
||||
|
||||
def select_resolution():
|
||||
"""Prompts input of type click.Choice from user. Presents options from QUALITIES constant.
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ import cloup
|
|||
from ...constants import CONTEXT_SETTINGS, EPILOG
|
||||
from ...plugins.plugins_flags import list_plugins
|
||||
|
||||
__all__ = ["plugins"]
|
||||
|
||||
|
||||
@cloup.command(
|
||||
context_settings=CONTEXT_SETTINGS,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ from .global_options import global_options
|
|||
from .output_options import output_options
|
||||
from .render_options import render_options
|
||||
|
||||
__all__ = ["render"]
|
||||
|
||||
|
||||
@cloup.command(
|
||||
context_settings=None,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ from __future__ import annotations
|
|||
|
||||
from cloup import Choice, option, option_group
|
||||
|
||||
__all__ = ["ease_of_access_options"]
|
||||
|
||||
ease_of_access_options = option_group(
|
||||
"Ease of access options",
|
||||
option(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from cloup import Choice, option, option_group
|
|||
|
||||
from ... import logger
|
||||
|
||||
__all__ = ["global_options"]
|
||||
|
||||
|
||||
def validate_gui_location(ctx, param, value):
|
||||
if value:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ from __future__ import annotations
|
|||
|
||||
from cloup import IntRange, Path, option, option_group
|
||||
|
||||
__all__ = ["output_options"]
|
||||
|
||||
output_options = option_group(
|
||||
"Output options",
|
||||
option(
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ from manim.constants import QUALITIES, RendererType
|
|||
|
||||
from ... import logger
|
||||
|
||||
__all__ = ["render_options"]
|
||||
|
||||
|
||||
def validate_scene_range(ctx, param, value):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import numpy as np
|
|||
from cloup import Context
|
||||
from PIL.Image import Resampling
|
||||
|
||||
from manim.typing import Vector3
|
||||
from manim.typing import Vector3D
|
||||
|
||||
__all__ = [
|
||||
"SCENE_NOT_FOUND_MESSAGE",
|
||||
|
|
@ -123,43 +123,43 @@ RESAMPLING_ALGORITHMS = {
|
|||
}
|
||||
|
||||
# Geometry: directions
|
||||
ORIGIN: Vector3 = np.array((0.0, 0.0, 0.0))
|
||||
ORIGIN: Vector3D = np.array((0.0, 0.0, 0.0))
|
||||
"""The center of the coordinate system."""
|
||||
|
||||
UP: Vector3 = np.array((0.0, 1.0, 0.0))
|
||||
UP: Vector3D = np.array((0.0, 1.0, 0.0))
|
||||
"""One unit step in the positive Y direction."""
|
||||
|
||||
DOWN: Vector3 = np.array((0.0, -1.0, 0.0))
|
||||
DOWN: Vector3D = np.array((0.0, -1.0, 0.0))
|
||||
"""One unit step in the negative Y direction."""
|
||||
|
||||
RIGHT: Vector3 = np.array((1.0, 0.0, 0.0))
|
||||
RIGHT: Vector3D = np.array((1.0, 0.0, 0.0))
|
||||
"""One unit step in the positive X direction."""
|
||||
|
||||
LEFT: Vector3 = np.array((-1.0, 0.0, 0.0))
|
||||
LEFT: Vector3D = np.array((-1.0, 0.0, 0.0))
|
||||
"""One unit step in the negative X direction."""
|
||||
|
||||
IN: Vector3 = np.array((0.0, 0.0, -1.0))
|
||||
IN: Vector3D = np.array((0.0, 0.0, -1.0))
|
||||
"""One unit step in the negative Z direction."""
|
||||
|
||||
OUT: Vector3 = np.array((0.0, 0.0, 1.0))
|
||||
OUT: Vector3D = np.array((0.0, 0.0, 1.0))
|
||||
"""One unit step in the positive Z direction."""
|
||||
|
||||
# Geometry: axes
|
||||
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))
|
||||
X_AXIS: Vector3D = np.array((1.0, 0.0, 0.0))
|
||||
Y_AXIS: Vector3D = np.array((0.0, 1.0, 0.0))
|
||||
Z_AXIS: Vector3D = np.array((0.0, 0.0, 1.0))
|
||||
|
||||
# Geometry: useful abbreviations for diagonals
|
||||
UL: Vector3 = UP + LEFT
|
||||
UL: Vector3D = UP + LEFT
|
||||
"""One step up plus one step left."""
|
||||
|
||||
UR: Vector3 = UP + RIGHT
|
||||
UR: Vector3D = UP + RIGHT
|
||||
"""One step up plus one step right."""
|
||||
|
||||
DL: Vector3 = DOWN + LEFT
|
||||
DL: Vector3D = DOWN + LEFT
|
||||
"""One step down plus one step left."""
|
||||
|
||||
DR: Vector3 = DOWN + RIGHT
|
||||
DR: Vector3D = DOWN + RIGHT
|
||||
"""One step down plus one step right."""
|
||||
|
||||
# Geometry
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ except ImportError:
|
|||
from .. import __version__, config
|
||||
from ..utils.module_ops import scene_classes_from_file
|
||||
|
||||
__all__ = ["configure_pygui"]
|
||||
|
||||
if dearpygui_imported:
|
||||
dpg.create_context()
|
||||
window = dpg.generate_uuid()
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ if TYPE_CHECKING:
|
|||
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
|
||||
from manim.typing import CubicBezierPoints, Point3D, QuadraticBezierPoints, Vector3D
|
||||
|
||||
|
||||
class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
||||
|
|
@ -91,12 +91,12 @@ class TipableVMobject(VMobject, metaclass=ConvertToOpenGL):
|
|||
def __init__(
|
||||
self,
|
||||
tip_length: float = DEFAULT_ARROW_TIP_LENGTH,
|
||||
normal_vector: Vector = OUT,
|
||||
normal_vector: Vector3D = OUT,
|
||||
tip_style: dict = {},
|
||||
**kwargs,
|
||||
) -> None:
|
||||
self.tip_length: float = tip_length
|
||||
self.normal_vector: Vector = normal_vector
|
||||
self.normal_vector: Vector3D = normal_vector
|
||||
self.tip_style: dict = tip_style
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import numpy as np
|
||||
from pathops import Path as SkiaPath
|
||||
from pathops import PathVerb, difference, intersection, union, xor
|
||||
|
|
@ -9,7 +11,9 @@ 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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.typing import Point2D_Array, Point3D_Array
|
||||
|
||||
from ...constants import RendererType
|
||||
|
||||
|
|
@ -26,21 +30,21 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|||
self,
|
||||
points: Point2D_Array,
|
||||
z_dim: float = 0.0,
|
||||
) -> list[np.ndarray]:
|
||||
"""Converts an iterable with coordinates in 2d to 3d by adding
|
||||
:attr:`z_dim` as the z coordinate.
|
||||
) -> Point3D_Array:
|
||||
"""Converts an iterable with coordinates in 2D to 3D by adding
|
||||
:attr:`z_dim` as the Z coordinate.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
points:
|
||||
An iterable which has the coordinates.
|
||||
An iterable of points.
|
||||
z_dim:
|
||||
The default value of z coordinate.
|
||||
Default value for the Z coordinate.
|
||||
|
||||
Returns
|
||||
-------
|
||||
Point2D_Array
|
||||
A list of array converted to 3d.
|
||||
Point3D_Array
|
||||
A list of the points converted to 3D.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
|
@ -66,7 +70,7 @@ class _BooleanOps(VMobject, metaclass=ConvertToOpenGL):
|
|||
|
||||
Returns
|
||||
-------
|
||||
SkiaPath:
|
||||
SkiaPath
|
||||
The converted path.
|
||||
"""
|
||||
path = SkiaPath()
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ 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.typing import Point2D, Point3D, Vector3D
|
||||
from manim.utils.color import ParsableManimColor
|
||||
|
||||
from ..matrix import Matrix # Avoid circular import
|
||||
|
|
@ -107,7 +107,7 @@ class Line(TipableVMobject):
|
|||
def _pointify(
|
||||
self,
|
||||
mob_or_point: Mobject | Point3D,
|
||||
direction: Vector | None = None,
|
||||
direction: Vector3D | None = None,
|
||||
) -> Point3D:
|
||||
"""Transforms a mobject into its corresponding point. Does nothing if a point is passed.
|
||||
|
||||
|
|
@ -163,16 +163,16 @@ class Line(TipableVMobject):
|
|||
self.generate_points()
|
||||
return super().put_start_and_end_on(start, end)
|
||||
|
||||
def get_vector(self) -> Vector:
|
||||
def get_vector(self) -> Vector3D:
|
||||
return self.get_end() - self.get_start()
|
||||
|
||||
def get_unit_vector(self) -> Vector:
|
||||
def get_unit_vector(self) -> Vector3D:
|
||||
return normalize(self.get_vector())
|
||||
|
||||
def get_angle(self) -> float:
|
||||
return angle_of_vector(self.get_vector())
|
||||
|
||||
def get_projection(self, point: Point3D) -> Vector:
|
||||
def get_projection(self, point: Point3D) -> Vector3D:
|
||||
"""Returns the projection of a point onto a line.
|
||||
|
||||
Parameters
|
||||
|
|
@ -579,7 +579,7 @@ class Arrow(Line):
|
|||
self.add_tip(tip=old_tips[1], at_start=True)
|
||||
return self
|
||||
|
||||
def get_normal_vector(self) -> Vector:
|
||||
def get_normal_vector(self) -> Vector3D:
|
||||
"""Returns the normal of a vector.
|
||||
|
||||
Examples
|
||||
|
|
@ -632,6 +632,11 @@ class Arrow(Line):
|
|||
class Vector(Arrow):
|
||||
"""A vector specialized for use in graphs.
|
||||
|
||||
.. caution::
|
||||
Do not confuse with the :class:`~.Vector2D`,
|
||||
:class:`~.Vector3D` or :class:`~.VectorND` type aliases,
|
||||
which are not Mobjects!
|
||||
|
||||
Parameters
|
||||
----------
|
||||
direction
|
||||
|
|
@ -654,7 +659,7 @@ class Vector(Arrow):
|
|||
self.add(plane, vector_1, vector_2)
|
||||
"""
|
||||
|
||||
def __init__(self, direction: Vector = RIGHT, buff: float = 0, **kwargs) -> None:
|
||||
def __init__(self, direction: Vector3D = RIGHT, buff: float = 0, **kwargs) -> None:
|
||||
self.buff = buff
|
||||
if len(direction) == 2:
|
||||
direction = np.hstack([direction, 0])
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ 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
|
||||
from manim.typing import Point3D, Vector3D
|
||||
|
||||
|
||||
class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
||||
|
|
@ -149,7 +149,7 @@ class ArrowTip(VMobject, metaclass=ConvertToOpenGL):
|
|||
return self.points[0]
|
||||
|
||||
@property
|
||||
def vector(self) -> Vector:
|
||||
def vector(self) -> Vector3D:
|
||||
r"""The vector pointing from the base point to the tip point.
|
||||
|
||||
Examples
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ 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
|
||||
from manim.typing import ManimFloat, Point2D, Point3D, Vector3D
|
||||
|
||||
LineType = TypeVar("LineType", bound=Line)
|
||||
|
||||
|
|
@ -2342,7 +2342,7 @@ class ThreeDAxes(Axes):
|
|||
y_length: float | None = config.frame_height + 2.5,
|
||||
z_length: float | None = config.frame_height - 1.5,
|
||||
z_axis_config: dict[str, Any] | None = None,
|
||||
z_normal: Vector3 = DOWN,
|
||||
z_normal: Vector3D = DOWN,
|
||||
num_axis_pieces: int = 20,
|
||||
light_source: Sequence[float] = 9 * DOWN + 7 * LEFT + 10 * OUT,
|
||||
# opengl stuff (?)
|
||||
|
|
@ -2433,7 +2433,7 @@ class ThreeDAxes(Axes):
|
|||
direction: Sequence[float] = UR,
|
||||
buff: float = SMALL_BUFF,
|
||||
rotation: float = PI / 2,
|
||||
rotation_axis: Vector3 = OUT,
|
||||
rotation_axis: Vector3D = OUT,
|
||||
**kwargs,
|
||||
) -> Mobject:
|
||||
"""Generate a y-axis label.
|
||||
|
|
@ -2480,11 +2480,11 @@ class ThreeDAxes(Axes):
|
|||
def get_z_axis_label(
|
||||
self,
|
||||
label: float | str | Mobject,
|
||||
edge: Vector3 = OUT,
|
||||
direction: Vector3 = RIGHT,
|
||||
edge: Vector3D = OUT,
|
||||
direction: Vector3D = RIGHT,
|
||||
buff: float = SMALL_BUFF,
|
||||
rotation: float = PI / 2,
|
||||
rotation_axis: Vector3 = RIGHT,
|
||||
rotation_axis: Vector3D = RIGHT,
|
||||
**kwargs: Any,
|
||||
) -> Mobject:
|
||||
"""Generate a z-axis label.
|
||||
|
|
|
|||
|
|
@ -56,8 +56,7 @@ if TYPE_CHECKING:
|
|||
PathFuncType,
|
||||
Point3D,
|
||||
Point3D_Array,
|
||||
Vector,
|
||||
Vector3,
|
||||
Vector3D,
|
||||
)
|
||||
|
||||
from ..animation.animation import Animation
|
||||
|
|
@ -1154,7 +1153,7 @@ class Mobject:
|
|||
for mob in self.family_members_with_points():
|
||||
func(mob)
|
||||
|
||||
def shift(self, *vectors: Vector3) -> Self:
|
||||
def shift(self, *vectors: Vector3D) -> Self:
|
||||
"""Shift by the given vectors.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1226,14 +1225,14 @@ class Mobject:
|
|||
)
|
||||
return self
|
||||
|
||||
def rotate_about_origin(self, angle: float, axis: Vector3 = OUT, axes=[]) -> Self:
|
||||
def rotate_about_origin(self, angle: float, axis: Vector3D = OUT, axes=[]) -> Self:
|
||||
"""Rotates the :class:`~.Mobject` about the ORIGIN, which is at [0,0,0]."""
|
||||
return self.rotate(angle, axis, about_point=ORIGIN)
|
||||
|
||||
def rotate(
|
||||
self,
|
||||
angle: float,
|
||||
axis: Vector3 = OUT,
|
||||
axis: Vector3D = OUT,
|
||||
about_point: Point3D | None = None,
|
||||
**kwargs,
|
||||
) -> Self:
|
||||
|
|
@ -1244,7 +1243,7 @@ class Mobject:
|
|||
)
|
||||
return self
|
||||
|
||||
def flip(self, axis: Vector3 = UP, **kwargs) -> Self:
|
||||
def flip(self, axis: Vector3D = UP, **kwargs) -> Self:
|
||||
"""Flips/Mirrors an mobject about its center.
|
||||
|
||||
Examples
|
||||
|
|
@ -1390,7 +1389,7 @@ class Mobject:
|
|||
return self
|
||||
|
||||
def align_on_border(
|
||||
self, direction: Vector3, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
self, direction: Vector3D, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
) -> Self:
|
||||
"""Direction just needs to be a vector pointing towards side or
|
||||
corner in the 2d plane.
|
||||
|
|
@ -1407,7 +1406,7 @@ class Mobject:
|
|||
return self
|
||||
|
||||
def to_corner(
|
||||
self, corner: Vector3 = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
self, corner: Vector3D = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
) -> Self:
|
||||
"""Moves this :class:`~.Mobject` to the given corner of the screen.
|
||||
|
||||
|
|
@ -1435,7 +1434,7 @@ class Mobject:
|
|||
return self.align_on_border(corner, buff)
|
||||
|
||||
def to_edge(
|
||||
self, edge: Vector3 = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
self, edge: Vector3D = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
|
||||
) -> Self:
|
||||
"""Moves this :class:`~.Mobject` to the given edge of the screen,
|
||||
without affecting its position in the other dimension.
|
||||
|
|
@ -1467,12 +1466,12 @@ class Mobject:
|
|||
def next_to(
|
||||
self,
|
||||
mobject_or_point: Mobject | Point3D,
|
||||
direction: Vector3 = RIGHT,
|
||||
direction: Vector3D = RIGHT,
|
||||
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
||||
aligned_edge: Vector3 = ORIGIN,
|
||||
aligned_edge: Vector3D = ORIGIN,
|
||||
submobject_to_align: Mobject | None = None,
|
||||
index_of_submobject_to_align: int | None = None,
|
||||
coor_mask: Vector3 = np.array([1, 1, 1]),
|
||||
coor_mask: Vector3D = np.array([1, 1, 1]),
|
||||
) -> Self:
|
||||
"""Move this :class:`~.Mobject` next to another's :class:`~.Mobject` or Point3D.
|
||||
|
||||
|
|
@ -1664,22 +1663,22 @@ class Mobject:
|
|||
|
||||
return self.rescale_to_fit(depth, 2, stretch=True, **kwargs)
|
||||
|
||||
def set_coord(self, value, dim: int, direction: Vector3 = ORIGIN) -> Self:
|
||||
def set_coord(self, value, dim: int, direction: Vector3D = ORIGIN) -> Self:
|
||||
curr = self.get_coord(dim, direction)
|
||||
shift_vect = np.zeros(self.dim)
|
||||
shift_vect[dim] = value - curr
|
||||
self.shift(shift_vect)
|
||||
return self
|
||||
|
||||
def set_x(self, x: float, direction: Vector3 = ORIGIN) -> Self:
|
||||
def set_x(self, x: float, direction: Vector3D = ORIGIN) -> Self:
|
||||
"""Set x value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
|
||||
return self.set_coord(x, 0, direction)
|
||||
|
||||
def set_y(self, y: float, direction: Vector3 = ORIGIN) -> Self:
|
||||
def set_y(self, y: float, direction: Vector3D = ORIGIN) -> Self:
|
||||
"""Set y value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
|
||||
return self.set_coord(y, 1, direction)
|
||||
|
||||
def set_z(self, z: float, direction: Vector3 = ORIGIN) -> Self:
|
||||
def set_z(self, z: float, direction: Vector3D = ORIGIN) -> Self:
|
||||
"""Set z value of the center of the :class:`~.Mobject` (``int`` or ``float``)"""
|
||||
return self.set_coord(z, 2, direction)
|
||||
|
||||
|
|
@ -1692,8 +1691,8 @@ class Mobject:
|
|||
def move_to(
|
||||
self,
|
||||
point_or_mobject: Point3D | Mobject,
|
||||
aligned_edge: Vector3 = ORIGIN,
|
||||
coor_mask: Vector3 = np.array([1, 1, 1]),
|
||||
aligned_edge: Vector3D = ORIGIN,
|
||||
coor_mask: Vector3D = np.array([1, 1, 1]),
|
||||
) -> Self:
|
||||
"""Move center of the :class:`~.Mobject` to certain Point3D."""
|
||||
if isinstance(point_or_mobject, Mobject):
|
||||
|
|
@ -1998,7 +1997,7 @@ class Mobject:
|
|||
else:
|
||||
return np.max(values)
|
||||
|
||||
def get_critical_point(self, direction: Vector3) -> Point3D:
|
||||
def get_critical_point(self, direction: Vector3D) -> Point3D:
|
||||
"""Picture a box bounding the :class:`~.Mobject`. Such a box has
|
||||
9 'critical points': 4 corners, 4 edge center, the
|
||||
center. This returns one of them, along the given direction.
|
||||
|
|
@ -2027,11 +2026,11 @@ class Mobject:
|
|||
|
||||
# Pseudonyms for more general get_critical_point method
|
||||
|
||||
def get_edge_center(self, direction: Vector3) -> Point3D:
|
||||
def get_edge_center(self, direction: Vector3D) -> Point3D:
|
||||
"""Get edge Point3Ds for certain direction."""
|
||||
return self.get_critical_point(direction)
|
||||
|
||||
def get_corner(self, direction: Vector3) -> Point3D:
|
||||
def get_corner(self, direction: Vector3D) -> Point3D:
|
||||
"""Get corner Point3Ds for certain direction."""
|
||||
return self.get_critical_point(direction)
|
||||
|
||||
|
|
@ -2042,7 +2041,7 @@ class Mobject:
|
|||
def get_center_of_mass(self) -> Point3D:
|
||||
return np.apply_along_axis(np.mean, 0, self.get_all_points())
|
||||
|
||||
def get_boundary_point(self, direction: Vector3) -> Point3D:
|
||||
def get_boundary_point(self, direction: Vector3D) -> Point3D:
|
||||
all_points = self.get_points_defining_boundary()
|
||||
index = np.argmax(np.dot(all_points, np.array(direction).T))
|
||||
return all_points[index]
|
||||
|
|
@ -2101,19 +2100,19 @@ class Mobject:
|
|||
dim,
|
||||
) - self.reduce_across_dimension(min, dim)
|
||||
|
||||
def get_coord(self, dim: int, direction: Vector3 = ORIGIN):
|
||||
def get_coord(self, dim: int, direction: Vector3D = ORIGIN):
|
||||
"""Meant to generalize ``get_x``, ``get_y`` and ``get_z``"""
|
||||
return self.get_extremum_along_dim(dim=dim, key=direction[dim])
|
||||
|
||||
def get_x(self, direction: Vector3 = ORIGIN) -> ManimFloat:
|
||||
def get_x(self, direction: Vector3D = ORIGIN) -> ManimFloat:
|
||||
"""Returns x Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
||||
return self.get_coord(0, direction)
|
||||
|
||||
def get_y(self, direction: Vector3 = ORIGIN) -> ManimFloat:
|
||||
def get_y(self, direction: Vector3D = ORIGIN) -> ManimFloat:
|
||||
"""Returns y Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
||||
return self.get_coord(1, direction)
|
||||
|
||||
def get_z(self, direction: Vector3 = ORIGIN) -> ManimFloat:
|
||||
def get_z(self, direction: Vector3D = ORIGIN) -> ManimFloat:
|
||||
"""Returns z Point3D of the center of the :class:`~.Mobject` as ``float``"""
|
||||
return self.get_coord(2, direction)
|
||||
|
||||
|
|
@ -2184,7 +2183,7 @@ class Mobject:
|
|||
return self.match_dim_size(mobject, 2, **kwargs)
|
||||
|
||||
def match_coord(
|
||||
self, mobject: Mobject, dim: int, direction: Vector3 = ORIGIN
|
||||
self, mobject: Mobject, dim: int, direction: Vector3D = ORIGIN
|
||||
) -> Self:
|
||||
"""Match the Point3Ds with the Point3Ds of another :class:`~.Mobject`."""
|
||||
return self.set_coord(
|
||||
|
|
@ -2208,7 +2207,7 @@ class Mobject:
|
|||
def align_to(
|
||||
self,
|
||||
mobject_or_point: Mobject | Point3D,
|
||||
direction: Vector3 = ORIGIN,
|
||||
direction: Vector3D = ORIGIN,
|
||||
) -> Self:
|
||||
"""Aligns mobject to another :class:`~.Mobject` in a certain direction.
|
||||
|
||||
|
|
@ -2263,7 +2262,7 @@ class Mobject:
|
|||
|
||||
def arrange(
|
||||
self,
|
||||
direction: Vector3 = RIGHT,
|
||||
direction: Vector3D = RIGHT,
|
||||
buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
|
||||
center: bool = True,
|
||||
**kwargs,
|
||||
|
|
@ -2296,7 +2295,7 @@ class Mobject:
|
|||
rows: int | None = None,
|
||||
cols: int | None = None,
|
||||
buff: float | tuple[float, float] = MED_SMALL_BUFF,
|
||||
cell_alignment: Vector3 = ORIGIN,
|
||||
cell_alignment: Vector3D = ORIGIN,
|
||||
row_alignments: str | None = None, # "ucd"
|
||||
col_alignments: str | None = None, # "lcr"
|
||||
row_heights: Iterable[float | None] | None = None,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVMobject
|
|||
|
||||
from ...constants import RendererType
|
||||
|
||||
__all__ = ["ConvertToOpenGL"]
|
||||
|
||||
|
||||
class ConvertToOpenGL(ABCMeta):
|
||||
"""Metaclass for swapping (V)Mobject with its OpenGL counterpart at runtime
|
||||
|
|
|
|||
|
|
@ -28,6 +28,32 @@ DEFAULT_DASH_LENGTH = 0.05
|
|||
DEFAULT_ARROW_TIP_LENGTH = 0.35
|
||||
DEFAULT_ARROW_TIP_WIDTH = 0.35
|
||||
|
||||
__all__ = [
|
||||
"OpenGLTipableVMobject",
|
||||
"OpenGLArc",
|
||||
"OpenGLArcBetweenPoints",
|
||||
"OpenGLCurvedArrow",
|
||||
"OpenGLCurvedDoubleArrow",
|
||||
"OpenGLCircle",
|
||||
"OpenGLDot",
|
||||
"OpenGLEllipse",
|
||||
"OpenGLAnnularSector",
|
||||
"OpenGLSector",
|
||||
"OpenGLAnnulus",
|
||||
"OpenGLLine",
|
||||
"OpenGLDashedLine",
|
||||
"OpenGLTangentLine",
|
||||
"OpenGLElbow",
|
||||
"OpenGLArrow",
|
||||
"OpenGLVector",
|
||||
"OpenGLDoubleArrow",
|
||||
"OpenGLCubicBezier",
|
||||
"OpenGLPolygon",
|
||||
"OpenGLRegularPolygon",
|
||||
"OpenGLTriangle",
|
||||
"OpenGLArrowTip",
|
||||
]
|
||||
|
||||
|
||||
class OpenGLTipableVMobject(OpenGLVMobject):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ from PIL.Image import Resampling
|
|||
from manim.mobject.opengl.opengl_surface import OpenGLSurface, OpenGLTexturedSurface
|
||||
from manim.utils.images import get_full_raster_image_path
|
||||
|
||||
__all__ = ["OpenGLImageMobject"]
|
||||
|
||||
|
||||
class OpenGLImageMobject(OpenGLTexturedSurface):
|
||||
def __init__(
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ def affects_shader_info_id(func):
|
|||
return wrapper
|
||||
|
||||
|
||||
__all__ = ["OpenGLMobject", "OpenGLGroup", "OpenGLPoint", "_AnimationBuilder"]
|
||||
|
||||
|
||||
class OpenGLMobject:
|
||||
"""Mathematical Object: base class for objects that can be displayed on screen.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from manim.utils.color import BLACK, WHITE, YELLOW, color_gradient, color_to_rgb
|
|||
from manim.utils.config_ops import _Uniforms
|
||||
from manim.utils.iterables import resize_with_interpolation
|
||||
|
||||
__all__ = ["OpenGLPMobject", "OpenGLPGroup", "OpenGLPMPoint"]
|
||||
|
||||
|
||||
class OpenGLPMobject(OpenGLMobject):
|
||||
shader_folder = "true_dot"
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ from manim.utils.images import change_to_rgba_array, get_full_raster_image_path
|
|||
from manim.utils.iterables import listify
|
||||
from manim.utils.space_ops import normalize_along_axis
|
||||
|
||||
__all__ = ["OpenGLSurface", "OpenGLTexturedSurface"]
|
||||
|
||||
|
||||
class OpenGLSurface(OpenGLMobject):
|
||||
r"""Creates a Surface.
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import numpy as np
|
|||
from manim.mobject.opengl.opengl_surface import OpenGLSurface
|
||||
from manim.mobject.opengl.opengl_vectorized_mobject import OpenGLVGroup, OpenGLVMobject
|
||||
|
||||
__all__ = ["OpenGLSurfaceMesh"]
|
||||
|
||||
|
||||
class OpenGLSurfaceMesh(OpenGLVGroup):
|
||||
def __init__(
|
||||
|
|
|
|||
|
|
@ -34,6 +34,15 @@ from manim.utils.space_ops import (
|
|||
z_to_vector,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"triggers_refreshed_triangulation",
|
||||
"OpenGLVMobject",
|
||||
"OpenGLVGroup",
|
||||
"OpenGLVectorizedPoint",
|
||||
"OpenGLCurvesAsSubmobjects",
|
||||
"OpenGLDashedVMobject",
|
||||
]
|
||||
|
||||
|
||||
def triggers_refreshed_triangulation(func):
|
||||
@wraps(func)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ from ...mobject.types.vectorized_mobject import VMobject
|
|||
from ...utils.color import BLACK
|
||||
from ..svg.svg_mobject import VMobjectFromSVGPath
|
||||
|
||||
__all__ = ["Brace", "BraceBetweenPoints", "BraceLabel", "ArcBrace"]
|
||||
|
||||
|
||||
class Brace(VMobjectFromSVGPath):
|
||||
"""Takes a mobject and draws a brace adjacent to it.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ from manim.mobject.text.text_mobject import Paragraph
|
|||
from manim.mobject.types.vectorized_mobject import VGroup
|
||||
from manim.utils.color import WHITE
|
||||
|
||||
__all__ = ["Code"]
|
||||
|
||||
|
||||
class Code(VGroup):
|
||||
"""A highlighted source code listing.
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ from manim.mobject.value_tracker import ValueTracker
|
|||
|
||||
string_to_mob_map = {}
|
||||
|
||||
__all__ = ["DecimalNumber", "Integer", "Variable"]
|
||||
|
||||
|
||||
class DecimalNumber(VMobject, metaclass=ConvertToOpenGL):
|
||||
"""An mobject representing a decimal number.
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ TEXT_MOB_SCALE_FACTOR = 0.05
|
|||
DEFAULT_LINE_SPACING_SCALE = 0.3
|
||||
TEXT2SVG_ADJUSTMENT_FACTOR = 4.8
|
||||
|
||||
__all__ = ["Text", "Paragraph", "MarkupText", "register_font"]
|
||||
|
||||
|
||||
def remove_invisible_chars(mobject: SVGMobject) -> SVGMobject:
|
||||
"""Function to remove unwanted invisible characters from some mobjects.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from manim.constants import ORIGIN, UP
|
|||
from manim.utils.space_ops import get_unit_normal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from manim.typing import Point3D, Vector
|
||||
from manim.typing import Point3D, Vector3D
|
||||
|
||||
|
||||
def get_3d_vmob_gradient_start_and_end_points(vmob) -> tuple[Point3D, Point3D]:
|
||||
|
|
@ -52,7 +52,7 @@ def get_3d_vmob_end_corner(vmob) -> Point3D:
|
|||
return vmob.points[get_3d_vmob_end_corner_index(vmob)]
|
||||
|
||||
|
||||
def get_3d_vmob_unit_normal(vmob, point_index: int) -> Vector:
|
||||
def get_3d_vmob_unit_normal(vmob, point_index: int) -> Vector3D:
|
||||
n_points = vmob.get_num_points()
|
||||
if len(vmob.get_anchors()) <= 2:
|
||||
return np.array(UP)
|
||||
|
|
@ -68,9 +68,9 @@ def get_3d_vmob_unit_normal(vmob, point_index: int) -> Vector:
|
|||
return unit_normal
|
||||
|
||||
|
||||
def get_3d_vmob_start_corner_unit_normal(vmob) -> Vector:
|
||||
def get_3d_vmob_start_corner_unit_normal(vmob) -> Vector3D:
|
||||
return get_3d_vmob_unit_normal(vmob, get_3d_vmob_start_corner_index(vmob))
|
||||
|
||||
|
||||
def get_3d_vmob_end_corner_unit_normal(vmob) -> Vector:
|
||||
def get_3d_vmob_end_corner_unit_normal(vmob) -> Vector3D:
|
||||
return get_3d_vmob_unit_normal(vmob, get_3d_vmob_end_corner_index(vmob))
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from manim.typing import Point3D, Vector3
|
||||
from manim.typing import Point3D, Vector3D
|
||||
from manim.utils.color import BLUE, BLUE_D, BLUE_E, LIGHT_GREY, WHITE, interpolate_color
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -953,7 +953,7 @@ class Line3D(Cylinder):
|
|||
def pointify(
|
||||
self,
|
||||
mob_or_point: Mobject | Point3D,
|
||||
direction: Vector3 = None,
|
||||
direction: Vector3D = None,
|
||||
) -> np.ndarray:
|
||||
"""Gets a point representing the center of the :class:`Mobjects <.Mobject>`.
|
||||
|
||||
|
|
@ -1001,7 +1001,7 @@ class Line3D(Cylinder):
|
|||
def parallel_to(
|
||||
cls,
|
||||
line: Line3D,
|
||||
point: Vector3 = ORIGIN,
|
||||
point: Vector3D = ORIGIN,
|
||||
length: float = 5,
|
||||
**kwargs,
|
||||
) -> Line3D:
|
||||
|
|
@ -1049,7 +1049,7 @@ class Line3D(Cylinder):
|
|||
def perpendicular_to(
|
||||
cls,
|
||||
line: Line3D,
|
||||
point: Vector3 = ORIGIN,
|
||||
point: Vector3D = ORIGIN,
|
||||
length: float = 5,
|
||||
**kwargs,
|
||||
) -> Line3D:
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ from ...utils.bezier import interpolate
|
|||
from ...utils.color import WHITE, ManimColor, color_to_int_rgb
|
||||
from ...utils.images import change_to_rgba_array, get_full_raster_image_path
|
||||
|
||||
__all__ = ["ImageMobject", "ImageMobjectFromCamera"]
|
||||
|
||||
|
||||
class AbstractImageMobject(Mobject):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ from ...utils.color import (
|
|||
)
|
||||
from ...utils.iterables import stretch_array_to_length
|
||||
|
||||
__all__ = ["PMobject", "Mobject1D", "Mobject2D", "PGroup", "PointCloudDot", "Point"]
|
||||
|
||||
|
||||
class PMobject(Mobject, metaclass=ConvertToOpenGL):
|
||||
"""A disc made of a cloud of Dots
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ if TYPE_CHECKING:
|
|||
Point3D_Array,
|
||||
QuadraticBezierPoints,
|
||||
RGBA_Array_Float,
|
||||
Vector3,
|
||||
Vector3D,
|
||||
Zeros,
|
||||
)
|
||||
|
||||
|
|
@ -74,6 +74,16 @@ if TYPE_CHECKING:
|
|||
# - Think about length of self.points. Always 0 or 1 mod 4?
|
||||
# That's kind of weird.
|
||||
|
||||
__all__ = [
|
||||
"VMobject",
|
||||
"VGroup",
|
||||
"VDict",
|
||||
"VectorizedPoint",
|
||||
"CurvesAsSubmobjects",
|
||||
"VectorizedPoint",
|
||||
"DashedVMobject",
|
||||
]
|
||||
|
||||
|
||||
class VMobject(Mobject):
|
||||
"""A vectorized mobject.
|
||||
|
|
@ -114,7 +124,7 @@ class VMobject(Mobject):
|
|||
background_stroke_width: float = 0,
|
||||
sheen_factor: float = 0.0,
|
||||
joint_type: LineJointType | None = None,
|
||||
sheen_direction: Vector3 = UL,
|
||||
sheen_direction: Vector3D = UL,
|
||||
close_new_points: bool = False,
|
||||
pre_function_handle_to_anchor_scale_factor: float = 0.01,
|
||||
make_smooth_after_applying_functions: bool = False,
|
||||
|
|
@ -139,7 +149,7 @@ class VMobject(Mobject):
|
|||
self.joint_type: LineJointType = (
|
||||
LineJointType.AUTO if joint_type is None else joint_type
|
||||
)
|
||||
self.sheen_direction: Vector3 = sheen_direction
|
||||
self.sheen_direction: Vector3D = sheen_direction
|
||||
self.close_new_points: bool = close_new_points
|
||||
self.pre_function_handle_to_anchor_scale_factor: float = (
|
||||
pre_function_handle_to_anchor_scale_factor
|
||||
|
|
@ -386,7 +396,7 @@ class VMobject(Mobject):
|
|||
background_stroke_width: float | None = None,
|
||||
background_stroke_opacity: float | None = None,
|
||||
sheen_factor: float | None = None,
|
||||
sheen_direction: Vector3 | None = None,
|
||||
sheen_direction: Vector3D | None = None,
|
||||
background_image: Image | str | None = None,
|
||||
family: bool = True,
|
||||
) -> Self:
|
||||
|
|
@ -553,7 +563,7 @@ class VMobject(Mobject):
|
|||
|
||||
color = property(get_color, set_color)
|
||||
|
||||
def set_sheen_direction(self, direction: Vector3, family: bool = True) -> Self:
|
||||
def set_sheen_direction(self, direction: Vector3D, family: bool = True) -> Self:
|
||||
"""Sets the direction of the applied sheen.
|
||||
|
||||
Parameters
|
||||
|
|
@ -578,11 +588,11 @@ class VMobject(Mobject):
|
|||
for submob in self.get_family():
|
||||
submob.sheen_direction = direction
|
||||
else:
|
||||
self.sheen_direction: Vector3 = direction
|
||||
self.sheen_direction: Vector3D = direction
|
||||
return self
|
||||
|
||||
def rotate_sheen_direction(
|
||||
self, angle: float, axis: Vector3 = OUT, family: bool = True
|
||||
self, angle: float, axis: Vector3D = OUT, family: bool = True
|
||||
) -> Self:
|
||||
"""Rotates the direction of the applied sheen.
|
||||
|
||||
|
|
@ -615,7 +625,7 @@ class VMobject(Mobject):
|
|||
return self
|
||||
|
||||
def set_sheen(
|
||||
self, factor: float, direction: Vector3 | None = None, family: bool = True
|
||||
self, factor: float, direction: Vector3D | None = None, family: bool = True
|
||||
) -> Self:
|
||||
"""Applies a color gradient from a direction.
|
||||
|
||||
|
|
@ -653,7 +663,7 @@ class VMobject(Mobject):
|
|||
self.set_fill(self.get_fill_color(), family=family)
|
||||
return self
|
||||
|
||||
def get_sheen_direction(self) -> Vector3:
|
||||
def get_sheen_direction(self) -> Vector3D:
|
||||
return np.array(self.sheen_direction)
|
||||
|
||||
def get_sheen_factor(self) -> float:
|
||||
|
|
@ -1028,7 +1038,7 @@ class VMobject(Mobject):
|
|||
def rotate(
|
||||
self,
|
||||
angle: float,
|
||||
axis: Vector3 = OUT,
|
||||
axis: Vector3D = OUT,
|
||||
about_point: Point3D | None = None,
|
||||
**kwargs,
|
||||
) -> Self:
|
||||
|
|
@ -1993,8 +2003,14 @@ class VGroup(VMobject, metaclass=ConvertToOpenGL):
|
|||
(gr-circle_red).animate.shift(RIGHT)
|
||||
)
|
||||
"""
|
||||
if not all(isinstance(m, (VMobject, OpenGLVMobject)) for m in vmobjects):
|
||||
raise TypeError("All submobjects must be of type VMobject")
|
||||
for m in vmobjects:
|
||||
if not isinstance(m, (VMobject, OpenGLVMobject)):
|
||||
raise TypeError(
|
||||
f"All submobjects of {self.__class__.__name__} must be of type VMobject. "
|
||||
f"Got {repr(m)} ({type(m).__name__}) instead. "
|
||||
"You can try using `Group` instead."
|
||||
)
|
||||
|
||||
return super().add(*vmobjects)
|
||||
|
||||
def __add__(self, vmobject: VMobject) -> Self:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ if typing.TYPE_CHECKING:
|
|||
from manim.animation.animation import Animation
|
||||
from manim.scene.scene import Scene
|
||||
|
||||
__all__ = ["CairoRenderer"]
|
||||
|
||||
|
||||
class CairoRenderer:
|
||||
"""A renderer using Cairo.
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ from .vectorized_mobject_rendering import (
|
|||
render_opengl_vectorized_mobject_stroke,
|
||||
)
|
||||
|
||||
__all__ = ["OpenGLCamera", "OpenGLRenderer"]
|
||||
|
||||
|
||||
class OpenGLCamera(OpenGLMobject):
|
||||
euler_angles = _Data()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ from screeninfo import get_monitors
|
|||
|
||||
from .. import __version__, config
|
||||
|
||||
__all__ = ["Window"]
|
||||
|
||||
|
||||
class Window(PygletWindow):
|
||||
fullscreen = False
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ from .. import logger
|
|||
# of a dict holding all the relevant information
|
||||
# to that shader
|
||||
|
||||
__all__ = ["ShaderWrapper"]
|
||||
|
||||
|
||||
def get_shader_dir():
|
||||
return Path(__file__).parent / "shaders"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ from ..utils import opengl
|
|||
from ..utils.space_ops import cross2d, earclip_triangulation
|
||||
from .shader import Shader
|
||||
|
||||
__all__ = [
|
||||
"render_opengl_vectorized_mobject_fill",
|
||||
"render_opengl_vectorized_mobject_stroke",
|
||||
]
|
||||
|
||||
|
||||
def build_matrix_lists(mob):
|
||||
root_hierarchical_matrix = mob.hierarchical_model_matrix()
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ from typing import Any
|
|||
|
||||
from manim import get_video_metadata
|
||||
|
||||
__all__ = ["Section", "DefaultSectionType"]
|
||||
|
||||
|
||||
class DefaultSectionType(str, Enum):
|
||||
"""The type of a section can be used for third party applications.
|
||||
|
|
|
|||
654
manim/typing.py
654
manim/typing.py
|
|
@ -1,133 +1,633 @@
|
|||
"""Custom type definitions used in Manim.
|
||||
|
||||
.. admonition:: Note for developers
|
||||
:class: important
|
||||
|
||||
Around the source code there are multiple strings which look like this:
|
||||
|
||||
.. code-block::
|
||||
|
||||
'''
|
||||
[CATEGORY]
|
||||
<category_name>
|
||||
'''
|
||||
|
||||
All type aliases defined under those strings will be automatically
|
||||
classified under that category.
|
||||
|
||||
If you need to define a new category, respect the format described above.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from os import PathLike
|
||||
from typing import Callable, Tuple, Union
|
||||
from typing import Callable, Literal, Union
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
# Color Types
|
||||
__all__ = [
|
||||
"ManimFloat",
|
||||
"ManimInt",
|
||||
"ManimColorDType",
|
||||
"RGB_Array_Float",
|
||||
"RGB_Tuple_Float",
|
||||
"RGB_Array_Int",
|
||||
"RGB_Tuple_Int",
|
||||
"RGBA_Array_Float",
|
||||
"RGBA_Tuple_Float",
|
||||
"RGBA_Array_Int",
|
||||
"RGBA_Tuple_Int",
|
||||
"HSV_Array_Float",
|
||||
"HSV_Tuple_Float",
|
||||
"ManimColorInternal",
|
||||
"PointDType",
|
||||
"InternalPoint2D",
|
||||
"Point2D",
|
||||
"InternalPoint2D_Array",
|
||||
"Point2D_Array",
|
||||
"InternalPoint3D",
|
||||
"Point3D",
|
||||
"InternalPoint3D_Array",
|
||||
"Point3D_Array",
|
||||
"Vector2D",
|
||||
"Vector2D_Array",
|
||||
"Vector3D",
|
||||
"Vector3D_Array",
|
||||
"VectorND",
|
||||
"VectorND_Array",
|
||||
"RowVector",
|
||||
"ColVector",
|
||||
"MatrixMN",
|
||||
"Zeros",
|
||||
"QuadraticBezierPoints",
|
||||
"QuadraticBezierPoints_Array",
|
||||
"QuadraticBezierPath",
|
||||
"QuadraticSpline",
|
||||
"CubicBezierPoints",
|
||||
"CubicBezierPoints_Array",
|
||||
"CubicBezierPath",
|
||||
"CubicSpline",
|
||||
"BezierPoints",
|
||||
"BezierPoints_Array",
|
||||
"BezierPath",
|
||||
"Spline",
|
||||
"FlatBezierPoints",
|
||||
"FunctionOverride",
|
||||
"PathFuncType",
|
||||
"MappingFunction",
|
||||
"Image",
|
||||
"GrayscaleImage",
|
||||
"RGBImage",
|
||||
"RGBAImage",
|
||||
"StrPath",
|
||||
"StrOrBytesPath",
|
||||
]
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Primitive data types
|
||||
"""
|
||||
|
||||
ManimFloat: TypeAlias = np.float64
|
||||
ManimInt: TypeAlias = np.int64
|
||||
ManimColorDType: TypeAlias = ManimFloat
|
||||
"""A double-precision floating-point value (64 bits, or 8 bytes),
|
||||
according to the IEEE 754 standard.
|
||||
"""
|
||||
|
||||
RGB_Array_Float: TypeAlias = npt.NDArray[ManimFloat]
|
||||
RGB_Tuple_Float: TypeAlias = Tuple[float, float, float]
|
||||
ManimInt: TypeAlias = np.int64
|
||||
r"""A long integer (64 bits, or 8 bytes).
|
||||
|
||||
It can take values between :math:`-2^{63}` and :math:`+2^{63} - 1`,
|
||||
which expressed in base 10 is a range between around
|
||||
:math:`-9.223 \cdot 10^{18}` and :math:`+9.223 \cdot 10^{18}`.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Color types
|
||||
"""
|
||||
|
||||
ManimColorDType: TypeAlias = ManimFloat
|
||||
"""Data type used in :class:`~.ManimColorInternal`: a
|
||||
double-precision float between 0 and 1.
|
||||
"""
|
||||
|
||||
RGB_Array_Float: TypeAlias = npt.NDArray[ManimColorDType]
|
||||
"""``shape: (3,)``
|
||||
|
||||
A :class:`numpy.ndarray` of 3 floats between 0 and 1, representing a
|
||||
color in RGB format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, and
|
||||
Blue in the represented color.
|
||||
"""
|
||||
|
||||
RGB_Tuple_Float: TypeAlias = tuple[float, float, float]
|
||||
"""``shape: (3,)``
|
||||
|
||||
A tuple of 3 floats between 0 and 1, representing a color in RGB
|
||||
format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, and
|
||||
Blue in the represented color.
|
||||
"""
|
||||
|
||||
RGB_Array_Int: TypeAlias = npt.NDArray[ManimInt]
|
||||
RGB_Tuple_Int: TypeAlias = Tuple[int, int, int]
|
||||
"""``shape: (3,)``
|
||||
|
||||
RGBA_Array_Float: TypeAlias = npt.NDArray[ManimFloat]
|
||||
RGBA_Tuple_Float: TypeAlias = Tuple[float, float, float, float]
|
||||
A :class:`numpy.ndarray` of 3 integers between 0 and 255,
|
||||
representing a color in RGB format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, and
|
||||
Blue in the represented color.
|
||||
"""
|
||||
|
||||
RGB_Tuple_Int: TypeAlias = tuple[int, int, int]
|
||||
"""``shape: (3,)``
|
||||
|
||||
A tuple of 3 integers between 0 and 255, representing a color in RGB
|
||||
format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, and
|
||||
Blue in the represented color.
|
||||
"""
|
||||
|
||||
RGBA_Array_Float: TypeAlias = npt.NDArray[ManimColorDType]
|
||||
"""``shape: (4,)``
|
||||
|
||||
A :class:`numpy.ndarray` of 4 floats between 0 and 1, representing a
|
||||
color in RGBA format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, Blue
|
||||
and Alpha (opacity) in the represented color.
|
||||
"""
|
||||
|
||||
RGBA_Tuple_Float: TypeAlias = tuple[float, float, float, float]
|
||||
"""``shape: (4,)``
|
||||
|
||||
A tuple of 4 floats between 0 and 1, representing a color in RGBA
|
||||
format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, Blue
|
||||
and Alpha (opacity) in the represented color.
|
||||
"""
|
||||
|
||||
RGBA_Array_Int: TypeAlias = npt.NDArray[ManimInt]
|
||||
RGBA_Tuple_Int: TypeAlias = Tuple[int, int, int, int]
|
||||
"""``shape: (4,)``
|
||||
|
||||
A :class:`numpy.ndarray` of 4 integers between 0 and 255,
|
||||
representing a color in RGBA format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, Blue
|
||||
and Alpha (opacity) in the represented color.
|
||||
"""
|
||||
|
||||
RGBA_Tuple_Int: TypeAlias = tuple[int, int, int, int]
|
||||
"""``shape: (4,)``
|
||||
|
||||
A tuple of 4 integers between 0 and 255, representing a color in RGBA
|
||||
format.
|
||||
|
||||
Its components describe, in order, the intensity of Red, Green, Blue
|
||||
and Alpha (opacity) in the represented color.
|
||||
"""
|
||||
|
||||
HSV_Array_Float: TypeAlias = RGB_Array_Float
|
||||
"""``shape: (3,)``
|
||||
|
||||
A :class:`numpy.ndarray` of 3 floats between 0 and 1, representing a
|
||||
color in HSV (or HSB) format.
|
||||
|
||||
Its components describe, in order, the Hue, Saturation and Value (or
|
||||
Brightness) in the represented color.
|
||||
"""
|
||||
|
||||
HSV_Tuple_Float: TypeAlias = RGB_Tuple_Float
|
||||
"""``shape: (3,)``
|
||||
|
||||
ManimColorInternal: TypeAlias = npt.NDArray[ManimColorDType]
|
||||
A tuple of 3 floats between 0 and 1, representing a color in HSV (or
|
||||
HSB) format.
|
||||
|
||||
# Point Types
|
||||
Its components describe, in order, the Hue, Saturation and Value (or
|
||||
Brightness) in the represented color.
|
||||
"""
|
||||
|
||||
ManimColorInternal: TypeAlias = RGBA_Array_Float
|
||||
"""``shape: (4,)``
|
||||
|
||||
Internal color representation used by :class:`~.ManimColor`,
|
||||
following the RGBA format.
|
||||
|
||||
It is a :class:`numpy.ndarray` consisting of 4 floats between 0 and
|
||||
1, describing respectively the intensities of Red, Green, Blue and
|
||||
Alpha (opacity) in the represented color.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Point types
|
||||
"""
|
||||
|
||||
PointDType: TypeAlias = ManimFloat
|
||||
""" DType for all points. """
|
||||
"""Default type for arrays representing points: a double-precision
|
||||
floating point value.
|
||||
"""
|
||||
|
||||
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.
|
||||
"""``shape: (2,)``
|
||||
|
||||
A 2-dimensional point: ``[float, float]``.
|
||||
|
||||
.. note::
|
||||
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]`. """
|
||||
Point2D: TypeAlias = Union[InternalPoint2D, tuple[float, float]]
|
||||
"""``shape: (2,)``
|
||||
|
||||
A 2-dimensional point: ``[float, float]``.
|
||||
|
||||
Normally, a function or method which expects a `Point2D` as a
|
||||
parameter can handle being passed a `Point3D` instead.
|
||||
"""
|
||||
|
||||
InternalPoint2D_Array: TypeAlias = npt.NDArray[PointDType]
|
||||
"""``shape: (N, 3)``
|
||||
|
||||
An array of `Point2D` objects: ``[[float, float], ...]``.
|
||||
|
||||
.. note::
|
||||
This type alias is mostly made available for internal use, and
|
||||
only includes the NumPy type.
|
||||
"""
|
||||
|
||||
Point2D_Array: TypeAlias = Union[InternalPoint2D_Array, tuple[Point2D, ...]]
|
||||
"""``shape: (N, 2)``
|
||||
|
||||
An array of `Point2D` objects: ``[[float, float], ...]``.
|
||||
|
||||
Normally, a function or method which expects a `Point2D_Array` as a
|
||||
parameter can handle being passed a `Point3D_Array` instead.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
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.
|
||||
"""``shape: (3,)``
|
||||
|
||||
A 3-dimensional point: ``[float, float, float]``.
|
||||
|
||||
.. note::
|
||||
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]` """
|
||||
Point3D: TypeAlias = Union[InternalPoint3D, tuple[float, float, float]]
|
||||
"""``shape: (3,)``
|
||||
|
||||
# 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)
|
||||
A 3-dimensional point: ``[float, float, float]``.
|
||||
"""
|
||||
|
||||
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.
|
||||
"""``shape: (N, 3)``
|
||||
|
||||
An array of `Point3D` objects: ``[[float, float, float], ...]``.
|
||||
|
||||
.. note::
|
||||
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], ...]
|
||||
Point3D_Array: TypeAlias = Union[InternalPoint3D_Array, tuple[Point3D, ...]]
|
||||
"""``shape: (N, 3)``
|
||||
|
||||
An array of `Point3D` objects: ``[[float, float, float], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Vector types
|
||||
"""
|
||||
|
||||
Vector2D: TypeAlias = Point2D
|
||||
"""``shape: (2,)``
|
||||
|
||||
A 2-dimensional vector: ``[float, float]``.
|
||||
|
||||
Normally, a function or method which expects a `Vector2D` as a
|
||||
parameter can handle being passed a `Vector3D` instead.
|
||||
|
||||
.. caution::
|
||||
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow`
|
||||
VMobjects!
|
||||
"""
|
||||
|
||||
Vector2D_Array: TypeAlias = Point2D_Array
|
||||
"""``shape: (M, 2)``
|
||||
|
||||
An array of `Vector2D` objects: ``[[float, float], ...]``.
|
||||
|
||||
Normally, a function or method which expects a `Vector2D_Array` as a
|
||||
parameter can handle being passed a `Vector3D_Array` instead.
|
||||
"""
|
||||
|
||||
Vector3D: TypeAlias = Point3D
|
||||
"""``shape: (3,)``
|
||||
|
||||
A 3-dimensional vector: ``[float, float, float]``.
|
||||
|
||||
.. caution::
|
||||
Do not confuse with the :class:`~.Vector` or :class:`~.Arrow3D`
|
||||
VMobjects!
|
||||
"""
|
||||
|
||||
Vector3D_Array: TypeAlias = Point3D_Array
|
||||
"""``shape: (M, 3)``
|
||||
|
||||
An array of `Vector3D` objects: ``[[float, float, float], ...]``.
|
||||
"""
|
||||
|
||||
VectorND: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, ...]]
|
||||
"""``shape (N,)``
|
||||
|
||||
An :math:`N`-dimensional vector: ``[float, ...]``.
|
||||
|
||||
.. caution::
|
||||
Do not confuse with the :class:`~.Vector` VMobject! This type alias
|
||||
is named "VectorND" instead of "Vector" to avoid potential name
|
||||
collisions.
|
||||
"""
|
||||
|
||||
VectorND_Array: TypeAlias = Union[npt.NDArray[PointDType], tuple[VectorND, ...]]
|
||||
"""``shape (M, N)``
|
||||
|
||||
An array of `VectorND` objects: ``[[float, ...], ...]``.
|
||||
"""
|
||||
|
||||
RowVector: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float, ...]]]
|
||||
"""``shape: (1, N)``
|
||||
|
||||
A row vector: ``[[float, ...]]``.
|
||||
"""
|
||||
|
||||
ColVector: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float], ...]]
|
||||
"""``shape: (N, 1)``
|
||||
|
||||
A column vector: ``[[float], [float], ...]``.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Matrix types
|
||||
"""
|
||||
|
||||
MatrixMN: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[float, ...], ...]]
|
||||
"""``shape: (M, N)``
|
||||
|
||||
A matrix: ``[[float, ...], [float, ...], ...]``.
|
||||
"""
|
||||
|
||||
Zeros: TypeAlias = Union[npt.NDArray[PointDType], tuple[tuple[Literal[0], ...], ...]]
|
||||
"""``shape: (M, N)``
|
||||
|
||||
A `MatrixMN` filled with zeros, typically created with
|
||||
``numpy.zeros((M, N))``.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Bézier types
|
||||
"""
|
||||
|
||||
QuadraticBezierPoints: TypeAlias = Union[
|
||||
npt.NDArray[PointDType], tuple[Point3D, Point3D, Point3D]
|
||||
]
|
||||
""" `shape: (N,3)` An Array of Points in 3D Space `[[float, float, float], ...]`.
|
||||
"""``shape: (3, 3)``
|
||||
|
||||
(Please refer to the documentation of the function you are using for further type Information)
|
||||
A `Point3D_Array` of 3 control points for a single quadratic Bézier
|
||||
curve:
|
||||
``[[float, float, float], [float, float, float], [float, float, float]]``.
|
||||
"""
|
||||
|
||||
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)
|
||||
QuadraticBezierPoints_Array: TypeAlias = Union[
|
||||
npt.NDArray[PointDType], tuple[QuadraticBezierPoints, ...]
|
||||
]
|
||||
"""``shape: (N, 3, 3)``
|
||||
|
||||
An array of :math:`N` `QuadraticBezierPoints` objects:
|
||||
``[[[float, float, float], [float, float, float], [float, float, float]], ...]``.
|
||||
"""
|
||||
|
||||
# Vector Types
|
||||
Vector3: TypeAlias = npt.NDArray[PointDType]
|
||||
""" `shape: (3,)` A Vector `[float, float, float]`. """
|
||||
QuadraticBezierPath: TypeAlias = Point3D_Array
|
||||
"""``shape: (3*N, 3)``
|
||||
|
||||
Vector: TypeAlias = npt.NDArray[PointDType]
|
||||
""" `shape: (N,)` A Vector `[float, ...]`. """
|
||||
A `Point3D_Array` of :math:`3N` points, where each one of the
|
||||
:math:`N` consecutive blocks of 3 points represents a quadratic
|
||||
Bézier curve:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
RowVector: TypeAlias = npt.NDArray[PointDType]
|
||||
""" `shape: (1,N)` A Row Vector `[[float, ...]]`. """
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
ColVector: TypeAlias = npt.NDArray[PointDType]
|
||||
""" `shape: (N,1)` A Column Vector `[[float], [float], ...]`. """
|
||||
QuadraticSpline: TypeAlias = QuadraticBezierPath
|
||||
"""``shape: (3*N, 3)``
|
||||
|
||||
MatrixMN: TypeAlias = npt.NDArray[PointDType]
|
||||
""" `shape: (M,N)` A Matrix `[[float, ...], [float, ...], ...]`. """
|
||||
A special case of `QuadraticBezierPath` where all the :math:`N`
|
||||
quadratic Bézier curves are connected, forming a quadratic spline:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
Zeros: TypeAlias = npt.NDArray[ManimFloat]
|
||||
"""A Matrix of Zeros. Typically created with `numpy.zeros((M,N))`"""
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
# 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."""
|
||||
CubicBezierPoints: TypeAlias = Union[
|
||||
npt.NDArray[PointDType], tuple[Point3D, Point3D, Point3D, Point3D]
|
||||
]
|
||||
"""``shape: (4, 3)``
|
||||
|
||||
A `Point3D_Array` of 4 control points for a single cubic Bézier
|
||||
curve:
|
||||
``[[float, float, float], [float, float, float], [float, float, float], [float, float, float]]``.
|
||||
"""
|
||||
|
||||
CubicBezierPoints_Array: TypeAlias = Union[
|
||||
npt.NDArray[PointDType], tuple[CubicBezierPoints, ...]
|
||||
]
|
||||
"""``shape: (N, 4, 3)``
|
||||
|
||||
An array of :math:`N` `CubicBezierPoints` objects:
|
||||
``[[[float, float, float], [float, float, float], [float, float, float], [float, float, float]], ...]``.
|
||||
"""
|
||||
|
||||
CubicBezierPath: TypeAlias = Point3D_Array
|
||||
"""``shape: (4*N, 3)``
|
||||
|
||||
A `Point3D_Array` of :math:`4N` points, where each one of the
|
||||
:math:`N` consecutive blocks of 4 points represents a cubic Bézier
|
||||
curve:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
CubicSpline: TypeAlias = CubicBezierPath
|
||||
"""``shape: (4*N, 3)``
|
||||
|
||||
A special case of `CubicBezierPath` where all the :math:`N` cubic
|
||||
Bézier curves are connected, forming a quadratic spline:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
BezierPoints: TypeAlias = Point3D_Array
|
||||
r"""``shape: (PPC, 3)``
|
||||
|
||||
A `Point3D_Array` of :math:`\text{PPC}` control points
|
||||
(:math:`\text{PPC: Points Per Curve} = n + 1`) for a single
|
||||
:math:`n`-th degree Bézier curve:
|
||||
``[[float, float, float], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
BezierPoints_Array: TypeAlias = Union[npt.NDArray[PointDType], tuple[BezierPoints, ...]]
|
||||
r"""``shape: (N, PPC, 3)``
|
||||
|
||||
An array of :math:`N` `BezierPoints` objects containing
|
||||
:math:`\text{PPC}` `Point3D` objects each
|
||||
(:math:`\text{PPC: Points Per Curve} = n + 1`):
|
||||
``[[[float, float, float], ...], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
BezierPath: TypeAlias = Point3D_Array
|
||||
r"""``shape: (PPC*N, 3)``
|
||||
|
||||
A `Point3D_Array` of :math:`\text{PPC} \cdot N` points, where each
|
||||
one of the :math:`N` consecutive blocks of :math:`\text{PPC}` control
|
||||
points (:math:`\text{PPC: Points Per Curve} = n + 1`) represents a
|
||||
Bézier curve of :math:`n`-th degree:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
Spline: TypeAlias = BezierPath
|
||||
r"""``shape: (PPC*N, 3)``
|
||||
|
||||
A special case of `BezierPath` where all the :math:`N` Bézier curves
|
||||
consisting of :math:`\text{PPC}` `Point3D` objects
|
||||
(:math:`\text{PPC: Points Per Curve} = n + 1`) are connected, forming
|
||||
an :math:`n`-th degree spline:
|
||||
``[[float, float, float], ...], ...]``.
|
||||
|
||||
Please refer to the documentation of the function you are using for
|
||||
further type information.
|
||||
"""
|
||||
|
||||
FlatBezierPoints: TypeAlias = Union[npt.NDArray[PointDType], tuple[float, ...]]
|
||||
"""``shape: (3*PPC*N,)``
|
||||
|
||||
A flattened array of Bézier control points:
|
||||
``[float, ...]``.
|
||||
"""
|
||||
|
||||
|
||||
# Misc
|
||||
"""
|
||||
[CATEGORY]
|
||||
Function types
|
||||
"""
|
||||
|
||||
# Due to current limitations
|
||||
# (see https://github.com/python/mypy/issues/14656 / 8263),
|
||||
# we don't specify the first argument type (Mobject).
|
||||
# Nor are we able to specify the return type (Animation) since we cannot import
|
||||
# that here.
|
||||
FunctionOverride: TypeAlias = Callable
|
||||
"""Function type returning an :class:`~.Animation` for the specified
|
||||
:class:`~.Mobject`.
|
||||
"""
|
||||
|
||||
PathFuncType: TypeAlias = Callable[[Point3D, Point3D, float], Point3D]
|
||||
"""Function mapping two points and an alpha value to a new point"""
|
||||
"""Function mapping two `Point3D` objects and an alpha value to a new
|
||||
`Point3D`.
|
||||
"""
|
||||
|
||||
MappingFunction: TypeAlias = Callable[[Point3D], Point3D]
|
||||
"""A function mapping a Point3D to another 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]"
|
||||
"""
|
||||
[CATEGORY]
|
||||
Image types
|
||||
"""
|
||||
|
||||
Image: TypeAlias = npt.NDArray[ManimInt]
|
||||
"""``shape: (height, width) | (height, width, 3) | (height, width, 4)``
|
||||
|
||||
A rasterized image with a height of ``height`` pixels and a width of
|
||||
``width`` pixels.
|
||||
|
||||
Every value in the array is an integer from 0 to 255.
|
||||
|
||||
Every pixel is represented either by a single integer indicating its
|
||||
lightness (for greyscale images), an `RGB_Array_Int` or an
|
||||
`RGBA_Array_Int`.
|
||||
"""
|
||||
|
||||
GrayscaleImage: TypeAlias = Image
|
||||
"""``shape: (height, width)``
|
||||
|
||||
A 100% opaque grayscale `Image`, where every pixel value is a
|
||||
`ManimInt` indicating its lightness (black -> gray -> white).
|
||||
"""
|
||||
|
||||
RGBImage: TypeAlias = Image
|
||||
"""``shape: (height, width, 3)``
|
||||
|
||||
A 100% opaque `Image` in color, where every pixel value is an
|
||||
`RGB_Array_Int` object.
|
||||
"""
|
||||
|
||||
RGBAImage: TypeAlias = Image
|
||||
"""``shape: (height, width, 4)``
|
||||
|
||||
An `Image` in color where pixels can be transparent. Every pixel
|
||||
value is an `RGBA_Array_Int` object.
|
||||
"""
|
||||
|
||||
|
||||
"""
|
||||
[CATEGORY]
|
||||
Path types
|
||||
"""
|
||||
|
||||
StrPath: TypeAlias = Union[str, PathLike[str]]
|
||||
"""A string or :class:`os.PathLike` representing a path to a
|
||||
directory or file.
|
||||
"""
|
||||
|
||||
StrOrBytesPath: TypeAlias = Union[str, bytes, PathLike[str], PathLike[bytes]]
|
||||
"""A string, bytes or :class:`os.PathLike` object representing a path
|
||||
to a directory or file.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ from typing import Callable
|
|||
from .. import config, logger
|
||||
from ..utils.hashing import get_hash_from_play_call
|
||||
|
||||
__all__ = ["handle_caching_play"]
|
||||
|
||||
|
||||
def handle_caching_play(func: Callable[..., None]):
|
||||
"""Decorator that returns a wrapped version of func that will compute
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ from ...utils.space_ops import normalize
|
|||
|
||||
# import manim._config as _config
|
||||
|
||||
|
||||
re_hex = re.compile("((?<=#)|(?<=0x))[A-F0-9]{6,8}", re.IGNORECASE)
|
||||
|
||||
|
||||
|
|
@ -729,14 +728,17 @@ ParsableManimColor: TypeAlias = Union[
|
|||
RGBA_Array_Int,
|
||||
RGBA_Array_Float,
|
||||
]
|
||||
"""ParsableManimColor is the representation for all types that are parsable to a color in manim"""
|
||||
"""`ParsableManimColor` represents all the types which can be parsed
|
||||
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`
|
||||
"""Helper function for use in functional style programming.
|
||||
Refer to :meth:`to_rgb` in :class:`ManimColor`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
"""Utilities for building the Manim documentation.
|
||||
|
||||
For more information about the Manim documentation building, see:
|
||||
|
||||
- :doc:`/contributing/development`, specifically the ``Documentation``
|
||||
bullet point under :ref:`polishing-changes-and-submitting-a-pull-request`
|
||||
- :doc:`/contributing/docs`
|
||||
|
||||
.. autosummary::
|
||||
:toctree: ../reference
|
||||
|
||||
autoaliasattr_directive
|
||||
autocolor_directive
|
||||
manim_directive
|
||||
module_parsing
|
||||
|
||||
"""
|
||||
197
manim/utils/docbuild/autoaliasattr_directive.py
Normal file
197
manim/utils/docbuild/autoaliasattr_directive.py
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
"""A directive for documenting type aliases and other module-level attributes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive
|
||||
from docutils.statemachine import ViewList
|
||||
|
||||
from manim.utils.docbuild.module_parsing import parse_module_attributes
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.application import Sphinx
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
__all__ = ["AliasAttrDocumenter"]
|
||||
|
||||
|
||||
ALIAS_DOCS_DICT, DATA_DICT = parse_module_attributes()
|
||||
ALIAS_LIST = [
|
||||
alias_name
|
||||
for module_dict in ALIAS_DOCS_DICT.values()
|
||||
for category_dict in module_dict.values()
|
||||
for alias_name in category_dict.keys()
|
||||
]
|
||||
|
||||
|
||||
def smart_replace(base: str, alias: str, substitution: str) -> str:
|
||||
"""Auxiliary function for substituting type aliases into a base
|
||||
string, when there are overlaps between the aliases themselves.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
base
|
||||
The string in which the type aliases will be located and
|
||||
replaced.
|
||||
alias
|
||||
The substring to be substituted.
|
||||
substitution
|
||||
The string which will replace every occurrence of ``alias``.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The new string after the alias substitution.
|
||||
"""
|
||||
|
||||
occurrences = []
|
||||
len_alias = len(alias)
|
||||
len_base = len(base)
|
||||
condition = lambda char: (not char.isalnum()) and char != "_"
|
||||
|
||||
start = 0
|
||||
i = 0
|
||||
while True:
|
||||
i = base.find(alias, start)
|
||||
if i == -1:
|
||||
break
|
||||
if (i == 0 or condition(base[i - 1])) and (
|
||||
i + len_alias == len_base or condition(base[i + len_alias])
|
||||
):
|
||||
occurrences.append(i)
|
||||
start = i + len_alias
|
||||
|
||||
for o in occurrences[::-1]:
|
||||
base = base[:o] + substitution + base[o + len_alias :]
|
||||
|
||||
return base
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> None:
|
||||
app.add_directive("autoaliasattr", AliasAttrDocumenter)
|
||||
|
||||
|
||||
class AliasAttrDocumenter(Directive):
|
||||
"""Directive which replaces Sphinx's Autosummary for module-level
|
||||
attributes: instead, it manually crafts a new "Type Aliases"
|
||||
section, where all the module-level attributes which are explicitly
|
||||
annotated as :class:`TypeAlias` are considered as such, for their
|
||||
use all around the Manim docs.
|
||||
|
||||
These type aliases are separated from the "regular" module-level
|
||||
attributes, which get their traditional "Module Attributes"
|
||||
section autogenerated with Sphinx's Autosummary under "Type
|
||||
Aliases".
|
||||
|
||||
See ``docs/source/_templates/autosummary/module.rst`` to watch
|
||||
this directive in action.
|
||||
|
||||
See :func:`~.parse_module_attributes` for more information on how
|
||||
the modules are parsed to obtain the :class:`TypeAlias` information
|
||||
and separate it from the other attributes.
|
||||
"""
|
||||
|
||||
objtype = "autoaliasattr"
|
||||
required_arguments = 1
|
||||
has_content = True
|
||||
|
||||
def run(self) -> list[nodes.Element]:
|
||||
module_name = self.arguments[0]
|
||||
# Slice module_name[6:] to remove the "manim." prefix which is
|
||||
# not present in the keys of the DICTs
|
||||
module_alias_dict = ALIAS_DOCS_DICT.get(module_name[6:], None)
|
||||
module_attrs_list = DATA_DICT.get(module_name[6:], None)
|
||||
|
||||
content = nodes.container()
|
||||
|
||||
# Add "Type Aliases" section
|
||||
if module_alias_dict is not None:
|
||||
module_alias_section = nodes.section(ids=[f"{module_name}.alias"])
|
||||
content += module_alias_section
|
||||
|
||||
# Use a rubric (title-like), just like in `module.rst`
|
||||
module_alias_section += nodes.rubric(text="Type Aliases")
|
||||
|
||||
# category_name: str
|
||||
# category_dict: AliasCategoryDict = dict[str, AliasInfo]
|
||||
for category_name, category_dict in module_alias_dict.items():
|
||||
category_section = nodes.section(
|
||||
ids=[category_name.lower().replace(" ", "_")]
|
||||
)
|
||||
module_alias_section += category_section
|
||||
# category_name can be possibly "" for uncategorized aliases
|
||||
if category_name:
|
||||
category_section += nodes.title(text=category_name)
|
||||
|
||||
category_alias_container = nodes.container()
|
||||
category_section += category_alias_container
|
||||
|
||||
# alias_name: str
|
||||
# alias_info: AliasInfo = dict[str, str]
|
||||
# Contains "definition": str
|
||||
# Can possibly contain "doc": str
|
||||
for alias_name, alias_info in category_dict.items():
|
||||
# Replace all occurrences of type aliases in the
|
||||
# definition for automatic cross-referencing!
|
||||
alias_def = alias_info["definition"]
|
||||
for A in ALIAS_LIST:
|
||||
alias_def = smart_replace(alias_def, A, f":class:`~.{A}`")
|
||||
|
||||
# Using the `.. class::` directive is CRUCIAL, since
|
||||
# function/method parameters are always annotated via
|
||||
# classes - therefore Sphinx expects a class
|
||||
unparsed = ViewList(
|
||||
[
|
||||
f".. class:: {alias_name}",
|
||||
"",
|
||||
" .. parsed-literal::",
|
||||
"",
|
||||
f" {alias_def}",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
if "doc" in alias_info:
|
||||
# Replace all occurrences of type aliases in
|
||||
# the docs for automatic cross-referencing!
|
||||
alias_doc = alias_info["doc"]
|
||||
for A in ALIAS_LIST:
|
||||
alias_doc = alias_doc.replace(f"`{A}`", f":class:`~.{A}`")
|
||||
|
||||
# Add all the lines with 4 spaces behind, to consider all the
|
||||
# documentation as a paragraph INSIDE the `.. class::` block
|
||||
doc_lines = alias_doc.split("\n")
|
||||
unparsed.extend(ViewList([f" {line}" for line in doc_lines]))
|
||||
|
||||
# Parse the reST text into a fresh container
|
||||
# https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
|
||||
alias_container = nodes.container()
|
||||
self.state.nested_parse(unparsed, 0, alias_container)
|
||||
category_alias_container += alias_container
|
||||
|
||||
# Then, add the traditional "Module Attributes" section
|
||||
if module_attrs_list is not None:
|
||||
module_attrs_section = nodes.section(ids=[f"{module_name}.data"])
|
||||
content += module_attrs_section
|
||||
|
||||
# Use the same rubric (title-like) as in `module.rst`
|
||||
module_attrs_section += nodes.rubric(text="Module Attributes")
|
||||
# Let Sphinx Autosummary do its thing as always
|
||||
# Add all the attribute names with 4 spaces behind, so that
|
||||
# they're considered as INSIDE the `.. autosummary::` block
|
||||
unparsed = ViewList(
|
||||
[
|
||||
".. autosummary::",
|
||||
*(f" {attr}" for attr in module_attrs_list),
|
||||
]
|
||||
)
|
||||
|
||||
# Parse the reST text into a fresh container
|
||||
# https://www.sphinx-doc.org/en/master/extdev/markupapi.html#parsing-directive-content-as-rest
|
||||
data_container = nodes.container()
|
||||
self.state.nested_parse(unparsed, 0, data_container)
|
||||
module_attrs_section += data_container
|
||||
|
||||
return [content]
|
||||
|
|
@ -1,13 +1,20 @@
|
|||
"""A directive for documenting colors in Manim."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers.rst import Directive
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
from manim import ManimColor
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
__all__ = ["ManimColorModuleDocumenter"]
|
||||
|
||||
|
||||
def setup(app: Sphinx) -> None:
|
||||
app.add_directive("automanimcolormodule", ManimColorModuleDocumenter)
|
||||
|
|
@ -21,9 +28,7 @@ class ManimColorModuleDocumenter(Directive):
|
|||
def add_directive_header(self, sig: str) -> None:
|
||||
super().add_directive_header(sig)
|
||||
|
||||
def run(
|
||||
self,
|
||||
) -> None:
|
||||
def run(self) -> list[nodes.Element]:
|
||||
module_name = self.arguments[0]
|
||||
try:
|
||||
import importlib
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ import sys
|
|||
import textwrap
|
||||
from pathlib import Path
|
||||
from timeit import timeit
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import jinja2
|
||||
from docutils import nodes
|
||||
|
|
@ -97,7 +98,13 @@ from docutils.statemachine import StringList
|
|||
from manim import QUALITIES
|
||||
from manim import __version__ as manim_version
|
||||
|
||||
classnamedict = {}
|
||||
if TYPE_CHECKING:
|
||||
from sphinx.application import Sphinx
|
||||
|
||||
__all__ = ["ManimDirective"]
|
||||
|
||||
|
||||
classnamedict: dict[str, int] = {}
|
||||
|
||||
|
||||
class SkipManimNode(nodes.Admonition, nodes.Element):
|
||||
|
|
@ -110,13 +117,13 @@ class SkipManimNode(nodes.Admonition, nodes.Element):
|
|||
pass
|
||||
|
||||
|
||||
def visit(self, node, name=""):
|
||||
def visit(self: SkipManimNode, node: nodes.Element, name: str = "") -> None:
|
||||
self.visit_admonition(node, name)
|
||||
if not isinstance(node[0], nodes.title):
|
||||
node.insert(0, nodes.title("skip-manim", "Example Placeholder"))
|
||||
|
||||
|
||||
def depart(self, node):
|
||||
def depart(self: SkipManimNode, node: nodes.Element) -> None:
|
||||
self.depart_admonition(node)
|
||||
|
||||
|
||||
|
|
@ -162,7 +169,7 @@ class ManimDirective(Directive):
|
|||
}
|
||||
final_argument_whitespace = True
|
||||
|
||||
def run(self):
|
||||
def run(self) -> list[nodes.Element]:
|
||||
# Rendering is skipped if the tag skip-manim is present,
|
||||
# or if we are making the pot-files
|
||||
should_skip = (
|
||||
|
|
@ -341,7 +348,7 @@ class ManimDirective(Directive):
|
|||
rendering_times_file_path = Path("../rendering_times.csv")
|
||||
|
||||
|
||||
def _write_rendering_stats(scene_name, run_time, file_name):
|
||||
def _write_rendering_stats(scene_name: str, run_time: str, file_name: str) -> None:
|
||||
with rendering_times_file_path.open("a") as file:
|
||||
csv.writer(file).writerow(
|
||||
[
|
||||
|
|
@ -352,7 +359,7 @@ def _write_rendering_stats(scene_name, run_time, file_name):
|
|||
)
|
||||
|
||||
|
||||
def _log_rendering_times(*args):
|
||||
def _log_rendering_times(*args: tuple[Any]) -> None:
|
||||
if rendering_times_file_path.exists():
|
||||
with rendering_times_file_path.open() as file:
|
||||
data = list(csv.reader(file))
|
||||
|
|
@ -381,12 +388,12 @@ def _log_rendering_times(*args):
|
|||
print("")
|
||||
|
||||
|
||||
def _delete_rendering_times(*args):
|
||||
def _delete_rendering_times(*args: tuple[Any]) -> None:
|
||||
if rendering_times_file_path.exists():
|
||||
rendering_times_file_path.unlink()
|
||||
|
||||
|
||||
def setup(app):
|
||||
def setup(app: Sphinx) -> dict[str, Any]:
|
||||
app.add_node(SkipManimNode, html=(visit, depart))
|
||||
|
||||
setup.app = app
|
||||
|
|
|
|||
163
manim/utils/docbuild/module_parsing.py
Normal file
163
manim/utils/docbuild/module_parsing.py
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
"""Read and parse all the Manim modules and extract documentation from them."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
from typing_extensions import TypeAlias
|
||||
|
||||
__all__ = ["parse_module_attributes"]
|
||||
|
||||
|
||||
AliasInfo: TypeAlias = dict[str, str]
|
||||
"""Dictionary with a `definition` key containing the definition of
|
||||
a :class:`TypeAlias` as a string, and optionally a `doc` key containing
|
||||
the documentation for that alias, if it exists.
|
||||
"""
|
||||
|
||||
AliasCategoryDict: TypeAlias = dict[str, AliasInfo]
|
||||
"""Dictionary which holds an `AliasInfo` for every alias name in a same
|
||||
category.
|
||||
"""
|
||||
|
||||
ModuleLevelAliasDict: TypeAlias = dict[str, AliasCategoryDict]
|
||||
"""Dictionary containing every :class:`TypeAlias` defined in a module,
|
||||
classified by category in different `AliasCategoryDict` objects.
|
||||
"""
|
||||
|
||||
AliasDocsDict: TypeAlias = dict[str, ModuleLevelAliasDict]
|
||||
"""Dictionary which, for every module in Manim, contains documentation
|
||||
about their module-level attributes which are explicitly defined as
|
||||
:class:`TypeAlias`, separating them from the rest of attributes.
|
||||
"""
|
||||
|
||||
DataDict: TypeAlias = dict[str, list[str]]
|
||||
"""Type for a dictionary which, for every module, contains a list with
|
||||
the names of all their DOCUMENTED module-level attributes (identified
|
||||
by Sphinx via the ``data`` role, hence the name) which are NOT
|
||||
explicitly defined as :class:`TypeAlias`.
|
||||
"""
|
||||
|
||||
ALIAS_DOCS_DICT: AliasDocsDict = {}
|
||||
DATA_DICT: DataDict = {}
|
||||
|
||||
MANIM_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
|
||||
|
||||
def parse_module_attributes() -> tuple[AliasDocsDict, DataDict]:
|
||||
"""Read all files, generate Abstract Syntax Trees from them, and
|
||||
extract useful information about the type aliases defined in the
|
||||
files: the category they belong to, their definition and their
|
||||
description, separating them from the "regular" module attributes.
|
||||
|
||||
Returns
|
||||
-------
|
||||
ALIAS_DOCS_DICT : `AliasDocsDict`
|
||||
A dictionary containing the information from all the type
|
||||
aliases in Manim. See `AliasDocsDict` for more information.
|
||||
|
||||
DATA_DICT : `DataDict`
|
||||
A dictionary containing the names of all DOCUMENTED
|
||||
module-level attributes which are not a :class:`TypeAlias`.
|
||||
"""
|
||||
global ALIAS_DOCS_DICT
|
||||
global DATA_DICT
|
||||
|
||||
if ALIAS_DOCS_DICT or DATA_DICT:
|
||||
return ALIAS_DOCS_DICT, DATA_DICT
|
||||
|
||||
for module_path in MANIM_ROOT.rglob("*.py"):
|
||||
module_name = module_path.resolve().relative_to(MANIM_ROOT)
|
||||
module_name = list(module_name.parts)
|
||||
module_name[-1] = module_name[-1][:-3] # remove .py
|
||||
module_name = ".".join(module_name)
|
||||
|
||||
module_content = module_path.read_text(encoding="utf-8")
|
||||
|
||||
# For storing TypeAliases
|
||||
module_dict: ModuleLevelAliasDict = {}
|
||||
category_dict: AliasCategoryDict | None = None
|
||||
alias_info: AliasInfo | None = None
|
||||
|
||||
# For storing regular module attributes
|
||||
data_list: list[str] = []
|
||||
data_name: str | None = None
|
||||
|
||||
for node in ast.iter_child_nodes(ast.parse(module_content)):
|
||||
# If we encounter a string:
|
||||
if (
|
||||
type(node) is ast.Expr
|
||||
and type(node.value) is ast.Constant
|
||||
and type(node.value.value) is str
|
||||
):
|
||||
string = node.value.value.strip()
|
||||
# It can be the start of a category
|
||||
section_str = "[CATEGORY]"
|
||||
if string.startswith(section_str):
|
||||
category_name = string[len(section_str) :].strip()
|
||||
module_dict[category_name] = {}
|
||||
category_dict = module_dict[category_name]
|
||||
alias_info = None
|
||||
# or a docstring of the alias defined before
|
||||
elif alias_info:
|
||||
alias_info["doc"] = string
|
||||
# or a docstring of the module attribute defined before
|
||||
elif data_name:
|
||||
data_list.append(data_name)
|
||||
continue
|
||||
|
||||
# If we encounter an assignment annotated as "TypeAlias":
|
||||
if (
|
||||
type(node) is ast.AnnAssign
|
||||
and type(node.annotation) is ast.Name
|
||||
and node.annotation.id == "TypeAlias"
|
||||
and type(node.target) is ast.Name
|
||||
and node.value is not None
|
||||
):
|
||||
alias_name = node.target.id
|
||||
def_node = node.value
|
||||
# If it's an Union, replace it with vertical bar notation
|
||||
if (
|
||||
type(def_node) is ast.Subscript
|
||||
and type(def_node.value) is ast.Name
|
||||
and def_node.value.id == "Union"
|
||||
):
|
||||
definition = " | ".join(
|
||||
ast.unparse(elem) for elem in def_node.slice.elts
|
||||
)
|
||||
else:
|
||||
definition = ast.unparse(def_node)
|
||||
|
||||
definition = definition.replace("npt.", "")
|
||||
if category_dict is None:
|
||||
module_dict[""] = {}
|
||||
category_dict = module_dict[""]
|
||||
category_dict[alias_name] = {"definition": definition}
|
||||
alias_info = category_dict[alias_name]
|
||||
continue
|
||||
|
||||
# If here, the node is not a TypeAlias definition
|
||||
alias_info = None
|
||||
|
||||
# It could still be a module attribute definition.
|
||||
# Does the assignment have a target of type Name? Then
|
||||
# it could be considered a definition of a module attribute.
|
||||
if type(node) is ast.AnnAssign:
|
||||
target = node.target
|
||||
elif type(node) is ast.Assign and len(node.targets) == 1:
|
||||
target = node.targets[0]
|
||||
else:
|
||||
target = None
|
||||
|
||||
if type(target) is ast.Name:
|
||||
data_name = target.id
|
||||
else:
|
||||
data_name = None
|
||||
|
||||
if len(module_dict) > 0:
|
||||
ALIAS_DOCS_DICT[module_name] = module_dict
|
||||
if len(data_list) > 0:
|
||||
DATA_DICT[module_name] = data_list
|
||||
|
||||
return ALIAS_DOCS_DICT, DATA_DICT
|
||||
|
|
@ -1,5 +1,11 @@
|
|||
from __future__ import annotations
|
||||
|
||||
__all__ = [
|
||||
"EndSceneEarlyException",
|
||||
"RerunSceneException",
|
||||
"MultiAnimationOverrideException",
|
||||
]
|
||||
|
||||
|
||||
class EndSceneEarlyException(Exception):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ from typing import Iterable
|
|||
from ..mobject.mobject import Mobject
|
||||
from ..utils.iterables import remove_list_redundancies
|
||||
|
||||
__all__ = ["extract_mobject_family_members"]
|
||||
|
||||
|
||||
def extract_mobject_family_members(
|
||||
mobjects: Iterable[Mobject],
|
||||
|
|
|
|||
|
|
@ -2,6 +2,11 @@ from __future__ import annotations
|
|||
|
||||
import itertools as it
|
||||
|
||||
__all__ = [
|
||||
"extract_mobject_family_members",
|
||||
"restructure_list_to_exclude_certain_family_members",
|
||||
]
|
||||
|
||||
|
||||
def extract_mobject_family_members(mobject_list, only_those_with_points=False):
|
||||
result = list(it.chain(*(mob.get_family() for mob in mobject_list)))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ from .. import config, logger
|
|||
if typing.TYPE_CHECKING:
|
||||
from manim.scene.scene import Scene
|
||||
|
||||
__all__ = ["KEYS_TO_FILTER_OUT", "get_hash_from_play_call", "get_json"]
|
||||
|
||||
# Sometimes there are elements that are not suitable for hashing (too long or
|
||||
# run-dependent). This is used to filter them out.
|
||||
KEYS_TO_FILTER_OUT = {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ from manim.renderer.shader import shader_program_cache
|
|||
|
||||
from ..constants import RendererType
|
||||
|
||||
__all__ = ["ManimMagic"]
|
||||
|
||||
try:
|
||||
from IPython import get_ipython
|
||||
from IPython.core.interactiveshell import InteractiveShell
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from pathlib import Path
|
|||
from .. import config, console, constants, logger
|
||||
from ..scene.scene_file_writer import SceneFileWriter
|
||||
|
||||
__all__ = ["scene_classes_from_file"]
|
||||
|
||||
|
||||
def get_module(file_name: Path):
|
||||
if str(file_name) == "-":
|
||||
|
|
|
|||
|
|
@ -7,6 +7,20 @@ from .. import config
|
|||
|
||||
depth = 20
|
||||
|
||||
__all__ = [
|
||||
"matrix_to_shader_input",
|
||||
"orthographic_projection_matrix",
|
||||
"perspective_projection_matrix",
|
||||
"translation_matrix",
|
||||
"x_rotation_matrix",
|
||||
"y_rotation_matrix",
|
||||
"z_rotation_matrix",
|
||||
"rotate_in_place_matrix",
|
||||
"rotation_matrix",
|
||||
"scale_matrix",
|
||||
"view_matrix",
|
||||
]
|
||||
|
||||
|
||||
def matrix_to_shader_input(matrix):
|
||||
return tuple(matrix.T.ravel())
|
||||
|
|
|
|||
|
|
@ -2,7 +2,26 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
from manim.typing import Point3D_Array, Vector, Vector3
|
||||
import itertools as it
|
||||
from typing import TYPE_CHECKING, Sequence
|
||||
|
||||
import numpy as np
|
||||
from mapbox_earcut import triangulate_float32 as earcut
|
||||
from scipy.spatial.transform import Rotation
|
||||
|
||||
from manim.constants import DOWN, OUT, PI, RIGHT, TAU, UP, RendererType
|
||||
from manim.utils.iterables import adjacent_pairs
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
from manim.typing import (
|
||||
ManimFloat,
|
||||
Point3D_Array,
|
||||
Vector2D,
|
||||
Vector2D_Array,
|
||||
Vector3D,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"quaternion_mult",
|
||||
|
|
@ -38,22 +57,11 @@ __all__ = [
|
|||
]
|
||||
|
||||
|
||||
import itertools as it
|
||||
from typing import Sequence
|
||||
|
||||
import numpy as np
|
||||
from mapbox_earcut import triangulate_float32 as earcut
|
||||
from scipy.spatial.transform import Rotation
|
||||
|
||||
from ..constants import DOWN, OUT, PI, RIGHT, TAU, UP, RendererType
|
||||
from ..utils.iterables import adjacent_pairs
|
||||
|
||||
|
||||
def norm_squared(v: float) -> float:
|
||||
return np.dot(v, v)
|
||||
|
||||
|
||||
def cross(v1: Vector3, v2: Vector3) -> Vector3:
|
||||
def cross(v1: Vector3D, v2: Vector3D) -> Vector3D:
|
||||
return np.array(
|
||||
[
|
||||
v1[1] * v2[2] - v1[2] * v2[1],
|
||||
|
|
@ -114,7 +122,7 @@ def quaternion_from_angle_axis(
|
|||
|
||||
Returns
|
||||
-------
|
||||
List[float]
|
||||
list[float]
|
||||
Gives back a quaternion from the angle and axis
|
||||
"""
|
||||
if not axis_normalized:
|
||||
|
|
@ -369,7 +377,7 @@ def normalize_along_axis(array: np.ndarray, axis: np.ndarray) -> np.ndarray:
|
|||
return array
|
||||
|
||||
|
||||
def get_unit_normal(v1: Vector3, v2: Vector3, tol: float = 1e-6) -> Vector3:
|
||||
def get_unit_normal(v1: Vector3D, v2: Vector3D, tol: float = 1e-6) -> Vector3D:
|
||||
"""Gets the unit normal of the vectors.
|
||||
|
||||
Parameters
|
||||
|
|
@ -654,8 +662,9 @@ def shoelace_direction(x_y: np.ndarray) -> str:
|
|||
|
||||
|
||||
def cross2d(
|
||||
a: Sequence[Vector] | Vector, b: Sequence[Vector] | Vector
|
||||
) -> Sequence[float] | float:
|
||||
a: Vector2D | Vector2D_Array,
|
||||
b: Vector2D | Vector2D_Array,
|
||||
) -> ManimFloat | npt.NDArray[ManimFloat]:
|
||||
"""Compute the determinant(s) of the passed
|
||||
vector (sequences).
|
||||
|
||||
|
|
@ -746,9 +755,14 @@ def earclip_triangulation(verts: np.ndarray, ring_ends: list) -> list:
|
|||
|
||||
# Move the ring which j belongs to from the
|
||||
# attached list to the detached list
|
||||
new_ring = next(filter(lambda ring: ring[0] <= j < ring[-1], detached_rings))
|
||||
detached_rings.remove(new_ring)
|
||||
attached_rings.append(new_ring)
|
||||
new_ring = next(
|
||||
(ring for ring in detached_rings if ring[0] <= j < ring[-1]), None
|
||||
)
|
||||
if new_ring is not None:
|
||||
detached_rings.remove(new_ring)
|
||||
attached_rings.append(new_ring)
|
||||
else:
|
||||
raise Exception("Could not find a ring to attach")
|
||||
|
||||
# Setup linked list
|
||||
after = []
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
"""Utilities for Manim tests using `pytest <https://pytest.org>`_.
|
||||
|
||||
For more information about Manim testing, see:
|
||||
|
||||
- :doc:`/contributing/development`, specifically the ``Tests`` bullet
|
||||
point under :ref:`polishing-changes-and-submitting-a-pull-request`
|
||||
- :doc:`/contributing/testing`
|
||||
|
||||
.. autosummary::
|
||||
:toctree: ../reference
|
||||
|
||||
frames_comparison
|
||||
_frames_testers
|
||||
_show_diff
|
||||
_test_class_makers
|
||||
|
||||
"""
|
||||
|
|
@ -19,6 +19,8 @@ from manim.utils.tex import TexTemplate
|
|||
|
||||
from .. import config, logger
|
||||
|
||||
__all__ = ["tex_to_svg_file"]
|
||||
|
||||
|
||||
def tex_hash(expression):
|
||||
id_str = str(expression)
|
||||
|
|
|
|||
1333
poetry.lock
generated
1333
poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -69,11 +69,11 @@ flake8-docstrings = "^1.7.0"
|
|||
flake8-pytest-style = "^1.7.2"
|
||||
flake8-simplify = "^0.14.1"
|
||||
flake8-rst-docstrings = "^0.3.0"
|
||||
furo = "^2022.06.21"
|
||||
furo = "^2023.09.10"
|
||||
gitpython = "^3"
|
||||
isort = "^5.12.0"
|
||||
matplotlib = "^3.8.2"
|
||||
myst-parser = "^0.17.2"
|
||||
myst-parser = "^2.0.0"
|
||||
pre-commit = "^3.5.0"
|
||||
psutil = {version = "^5.8.0", python = "<3.10"}
|
||||
psutil-wheels = {version = "5.8.0", python = ">=3.10"}
|
||||
|
|
@ -81,10 +81,10 @@ pytest = "^7.4.3"
|
|||
pygithub = "^2.1.1"
|
||||
pytest-cov = "^4.1.0"
|
||||
pytest-xdist = "^2.2" # Using latest gives flaky tests
|
||||
Sphinx = "^4"
|
||||
Sphinx = "^7.2.6"
|
||||
sphinx-copybutton = "^0.5.2"
|
||||
sphinxcontrib-programoutput = "^0.17"
|
||||
sphinxext-opengraph = "^0.9.0"
|
||||
sphinxext-opengraph = "^0.9.1"
|
||||
types-decorator = "^0.1.7"
|
||||
types-Pillow = "^10.1.0.2"
|
||||
types-Pygments = "^2.17.0.0"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
|
||||
from manim import *
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue