manim 0.18.1__py3-none-any.whl → 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of manim might be problematic. Click here for more details.
- manim/__main__.py +45 -12
- manim/_config/__init__.py +2 -2
- manim/_config/cli_colors.py +8 -4
- manim/_config/default.cfg +0 -2
- manim/_config/logger_utils.py +5 -0
- manim/_config/utils.py +29 -38
- manim/animation/animation.py +148 -8
- manim/animation/composition.py +16 -13
- manim/animation/creation.py +184 -8
- manim/animation/fading.py +5 -8
- manim/animation/indication.py +93 -26
- manim/animation/movement.py +21 -3
- manim/animation/rotation.py +2 -1
- manim/animation/specialized.py +3 -5
- manim/animation/speedmodifier.py +3 -3
- manim/animation/transform.py +4 -5
- manim/animation/updaters/mobject_update_utils.py +17 -14
- manim/camera/camera.py +2 -2
- manim/cli/__init__.py +17 -0
- manim/cli/cfg/group.py +52 -36
- manim/cli/checkhealth/checks.py +92 -76
- manim/cli/checkhealth/commands.py +12 -5
- manim/cli/default_group.py +148 -24
- manim/cli/init/commands.py +28 -23
- manim/cli/plugins/commands.py +13 -3
- manim/cli/render/commands.py +47 -42
- manim/cli/render/global_options.py +43 -9
- manim/cli/render/render_options.py +84 -19
- manim/constants.py +11 -4
- manim/mobject/frame.py +0 -1
- manim/mobject/geometry/arc.py +109 -75
- manim/mobject/geometry/boolean_ops.py +20 -17
- manim/mobject/geometry/labeled.py +300 -77
- manim/mobject/geometry/line.py +120 -60
- manim/mobject/geometry/polygram.py +109 -25
- manim/mobject/geometry/shape_matchers.py +35 -15
- manim/mobject/geometry/tips.py +36 -27
- manim/mobject/graph.py +48 -40
- manim/mobject/graphing/coordinate_systems.py +110 -45
- manim/mobject/graphing/functions.py +16 -10
- manim/mobject/graphing/number_line.py +23 -9
- manim/mobject/graphing/probability.py +2 -10
- manim/mobject/graphing/scale.py +6 -5
- manim/mobject/matrix.py +17 -19
- manim/mobject/mobject.py +149 -103
- manim/mobject/opengl/opengl_geometry.py +4 -8
- manim/mobject/opengl/opengl_mobject.py +506 -343
- manim/mobject/opengl/opengl_point_cloud_mobject.py +3 -7
- manim/mobject/opengl/opengl_surface.py +1 -2
- manim/mobject/opengl/opengl_vectorized_mobject.py +27 -65
- manim/mobject/svg/brace.py +61 -13
- manim/mobject/svg/svg_mobject.py +2 -1
- manim/mobject/table.py +11 -12
- manim/mobject/text/code_mobject.py +186 -550
- manim/mobject/text/numbers.py +7 -7
- manim/mobject/text/tex_mobject.py +22 -13
- manim/mobject/text/text_mobject.py +29 -20
- manim/mobject/three_d/polyhedra.py +98 -1
- manim/mobject/three_d/three_dimensions.py +59 -31
- manim/mobject/types/image_mobject.py +37 -23
- manim/mobject/types/point_cloud_mobject.py +103 -67
- manim/mobject/types/vectorized_mobject.py +387 -214
- manim/mobject/value_tracker.py +2 -1
- manim/mobject/vector_field.py +2 -4
- manim/opengl/__init__.py +3 -3
- manim/plugins/__init__.py +2 -3
- manim/plugins/plugins_flags.py +3 -3
- manim/renderer/cairo_renderer.py +11 -11
- manim/renderer/opengl_renderer.py +19 -20
- manim/renderer/shader.py +2 -3
- manim/renderer/shader_wrapper.py +3 -2
- manim/scene/moving_camera_scene.py +23 -0
- manim/scene/scene.py +72 -41
- manim/scene/scene_file_writer.py +313 -164
- manim/scene/section.py +15 -15
- manim/scene/three_d_scene.py +8 -15
- manim/scene/vector_space_scene.py +3 -6
- manim/typing.py +326 -66
- manim/utils/bezier.py +1658 -381
- manim/utils/caching.py +11 -5
- manim/utils/color/AS2700.py +2 -0
- manim/utils/color/BS381.py +2 -0
- manim/utils/color/DVIPSNAMES.py +96 -0
- manim/utils/color/SVGNAMES.py +179 -0
- manim/utils/color/X11.py +3 -0
- manim/utils/color/XKCD.py +2 -0
- manim/utils/color/__init__.py +8 -5
- manim/utils/color/core.py +818 -301
- manim/utils/color/manim_colors.py +7 -9
- manim/utils/commands.py +40 -19
- manim/utils/config_ops.py +18 -13
- manim/utils/debug.py +8 -6
- manim/utils/deprecation.py +92 -43
- manim/utils/docbuild/autoaliasattr_directive.py +45 -8
- manim/utils/docbuild/autocolor_directive.py +12 -13
- manim/utils/docbuild/manim_directive.py +35 -29
- manim/utils/docbuild/module_parsing.py +74 -27
- manim/utils/family.py +3 -3
- manim/utils/family_ops.py +12 -4
- manim/utils/file_ops.py +22 -16
- manim/utils/hashing.py +7 -7
- manim/utils/images.py +10 -4
- manim/utils/ipython_magic.py +12 -8
- manim/utils/iterables.py +161 -119
- manim/utils/module_ops.py +55 -19
- manim/utils/opengl.py +68 -23
- manim/utils/parameter_parsing.py +3 -2
- manim/utils/paths.py +11 -5
- manim/utils/polylabel.py +168 -0
- manim/utils/qhull.py +218 -0
- manim/utils/rate_functions.py +69 -32
- manim/utils/simple_functions.py +24 -15
- manim/utils/sounds.py +7 -1
- manim/utils/space_ops.py +48 -37
- manim/utils/testing/_frames_testers.py +13 -8
- manim/utils/testing/_show_diff.py +5 -3
- manim/utils/testing/_test_class_makers.py +33 -18
- manim/utils/testing/frames_comparison.py +20 -14
- manim/utils/tex.py +4 -2
- manim/utils/tex_file_writing.py +45 -45
- manim/utils/tex_templates.py +1 -1
- manim/utils/unit.py +6 -5
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/METADATA +16 -9
- manim-0.19.0.dist-info/RECORD +221 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/WHEEL +1 -1
- manim-0.18.1.dist-info/RECORD +0 -217
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/LICENSE.community +0 -0
- {manim-0.18.1.dist-info → manim-0.19.0.dist-info}/entry_points.txt +0 -0
manim/cli/default_group.py
CHANGED
|
@@ -6,68 +6,192 @@ In particular, this class is what allows ``manim`` to act as ``manim render``.
|
|
|
6
6
|
This is a vendored version of https://github.com/click-contrib/click-default-group/
|
|
7
7
|
under the BSD 3-Clause "New" or "Revised" License.
|
|
8
8
|
|
|
9
|
-
This library isn't used as a dependency as we need to inherit from
|
|
10
|
-
of
|
|
9
|
+
This library isn't used as a dependency, as we need to inherit from
|
|
10
|
+
:class:`cloup.Group` instead of :class:`click.Group`.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
13
15
|
import warnings
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
14
17
|
|
|
15
18
|
import cloup
|
|
16
19
|
|
|
20
|
+
from manim.utils.deprecation import deprecated
|
|
21
|
+
|
|
17
22
|
__all__ = ["DefaultGroup"]
|
|
18
23
|
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from click import Command, Context
|
|
26
|
+
|
|
19
27
|
|
|
20
28
|
class DefaultGroup(cloup.Group):
|
|
21
|
-
"""Invokes a subcommand marked with ``default=True`` if any subcommand not
|
|
29
|
+
"""Invokes a subcommand marked with ``default=True`` if any subcommand is not
|
|
22
30
|
chosen.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
*args
|
|
35
|
+
Positional arguments to forward to :class:`cloup.Group`.
|
|
36
|
+
**kwargs
|
|
37
|
+
Keyword arguments to forward to :class:`cloup.Group`. The keyword
|
|
38
|
+
``ignore_unknown_options`` must be set to ``False``.
|
|
39
|
+
|
|
40
|
+
Attributes
|
|
41
|
+
----------
|
|
42
|
+
default_cmd_name : str | None
|
|
43
|
+
The name of the default command, if specified through the ``default``
|
|
44
|
+
keyword argument. Otherwise, this is set to ``None``.
|
|
45
|
+
default_if_no_args : bool
|
|
46
|
+
Whether to include or not the default command, if no command arguments
|
|
47
|
+
are supplied. This can be specified through the ``default_if_no_args``
|
|
48
|
+
keyword argument. Default is ``False``.
|
|
23
49
|
"""
|
|
24
50
|
|
|
25
|
-
def __init__(self, *args, **kwargs):
|
|
51
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
|
26
52
|
# To resolve as the default command.
|
|
27
53
|
if not kwargs.get("ignore_unknown_options", True):
|
|
28
54
|
raise ValueError("Default group accepts unknown options")
|
|
29
55
|
self.ignore_unknown_options = True
|
|
30
|
-
self.default_cmd_name = kwargs.pop("default", None)
|
|
31
|
-
self.default_if_no_args = kwargs.pop("default_if_no_args", False)
|
|
56
|
+
self.default_cmd_name: str | None = kwargs.pop("default", None)
|
|
57
|
+
self.default_if_no_args: bool = kwargs.pop("default_if_no_args", False)
|
|
32
58
|
super().__init__(*args, **kwargs)
|
|
33
59
|
|
|
34
|
-
def set_default_command(self, command):
|
|
35
|
-
"""Sets a command function as the default command.
|
|
60
|
+
def set_default_command(self, command: Command) -> None:
|
|
61
|
+
"""Sets a command function as the default command.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
command
|
|
66
|
+
The command to set as default.
|
|
67
|
+
"""
|
|
36
68
|
cmd_name = command.name
|
|
37
69
|
self.add_command(command)
|
|
38
70
|
self.default_cmd_name = cmd_name
|
|
39
71
|
|
|
40
|
-
def parse_args(self, ctx, args):
|
|
41
|
-
|
|
72
|
+
def parse_args(self, ctx: Context, args: list[str]) -> list[str]:
|
|
73
|
+
"""Parses the list of ``args`` by forwarding it to
|
|
74
|
+
:meth:`cloup.Group.parse_args`. Before doing so, if
|
|
75
|
+
:attr:`default_if_no_args` is set to ``True`` and ``args`` is empty,
|
|
76
|
+
this function appends to it the name of the default command specified
|
|
77
|
+
by :attr:`default_cmd_name`.
|
|
78
|
+
|
|
79
|
+
Parameters
|
|
80
|
+
----------
|
|
81
|
+
ctx
|
|
82
|
+
The Click context.
|
|
83
|
+
args
|
|
84
|
+
A list of arguments. If it's empty and :attr:`default_if_no_args`
|
|
85
|
+
is ``True``, append the name of the default command to it.
|
|
86
|
+
|
|
87
|
+
Returns
|
|
88
|
+
-------
|
|
89
|
+
list[str]
|
|
90
|
+
The parsed arguments.
|
|
91
|
+
"""
|
|
92
|
+
if not args and self.default_if_no_args and self.default_cmd_name:
|
|
42
93
|
args.insert(0, self.default_cmd_name)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
94
|
+
parsed_args: list[str] = super().parse_args(ctx, args)
|
|
95
|
+
return parsed_args
|
|
96
|
+
|
|
97
|
+
def get_command(self, ctx: Context, cmd_name: str) -> Command | None:
|
|
98
|
+
"""Get a command function by its name, by forwarding the arguments to
|
|
99
|
+
:meth:`cloup.Group.get_command`. If ``cmd_name`` does not match any of
|
|
100
|
+
the command names in :attr:`commands`, attempt to get the default command
|
|
101
|
+
instead.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
ctx
|
|
106
|
+
The Click context.
|
|
107
|
+
cmd_name
|
|
108
|
+
The name of the command to get.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
:class:`click.Command` | None
|
|
113
|
+
The command, if found. Otherwise, ``None``.
|
|
114
|
+
"""
|
|
115
|
+
if cmd_name not in self.commands and self.default_cmd_name:
|
|
47
116
|
# No command name matched.
|
|
48
|
-
ctx.arg0 = cmd_name
|
|
117
|
+
ctx.meta["arg0"] = cmd_name
|
|
49
118
|
cmd_name = self.default_cmd_name
|
|
50
119
|
return super().get_command(ctx, cmd_name)
|
|
51
120
|
|
|
52
|
-
def resolve_command(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
121
|
+
def resolve_command(
|
|
122
|
+
self, ctx: Context, args: list[str]
|
|
123
|
+
) -> tuple[str | None, Command | None, list[str]]:
|
|
124
|
+
"""Given a list of ``args`` given by a CLI, find a command which
|
|
125
|
+
matches the first element, and return its name (``cmd_name``), the
|
|
126
|
+
command function itself (``cmd``) and the rest of the arguments which
|
|
127
|
+
shall be passed to the function (``cmd_args``). If not found, return
|
|
128
|
+
``None``, ``None`` and the rest of the arguments.
|
|
129
|
+
|
|
130
|
+
After resolving the command, if the Click context given by ``ctx``
|
|
131
|
+
contains an ``arg0`` attribute in its :attr:`click.Context.meta`
|
|
132
|
+
dictionary, insert it as the first element of the returned
|
|
133
|
+
``cmd_args``.
|
|
134
|
+
|
|
135
|
+
Parameters
|
|
136
|
+
----------
|
|
137
|
+
ctx
|
|
138
|
+
The Click context.
|
|
139
|
+
cmd_name
|
|
140
|
+
The name of the command to get.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
cmd_name : str | None
|
|
145
|
+
The command name, if found. Otherwise, ``None``.
|
|
146
|
+
cmd : :class:`click.Command` | None
|
|
147
|
+
The command, if found. Otherwise, ``None``.
|
|
148
|
+
cmd_args : list[str]
|
|
149
|
+
The rest of the arguments to be passed to ``cmd``.
|
|
150
|
+
"""
|
|
151
|
+
cmd_name, cmd, args = super().resolve_command(ctx, args)
|
|
152
|
+
if "arg0" in ctx.meta:
|
|
153
|
+
args.insert(0, ctx.meta["arg0"])
|
|
154
|
+
if cmd is not None:
|
|
155
|
+
cmd_name = cmd.name
|
|
58
156
|
return cmd_name, cmd, args
|
|
59
157
|
|
|
60
|
-
|
|
158
|
+
@deprecated
|
|
159
|
+
def command(
|
|
160
|
+
self, *args: Any, **kwargs: Any
|
|
161
|
+
) -> Callable[[Callable[..., object]], Command]:
|
|
162
|
+
"""Return a decorator which converts any function into the default
|
|
163
|
+
subcommand for this :class:`DefaultGroup`.
|
|
164
|
+
|
|
165
|
+
.. warning::
|
|
166
|
+
This method is deprecated. Use the ``default`` parameter of
|
|
167
|
+
:class:`DefaultGroup` or :meth:`set_default_command` instead.
|
|
168
|
+
|
|
169
|
+
Parameters
|
|
170
|
+
----------
|
|
171
|
+
*args
|
|
172
|
+
Positional arguments to pass to :meth:`cloup.Group.command`.
|
|
173
|
+
**kwargs
|
|
174
|
+
Keyword arguments to pass to :meth:`cloup.Group.command`.
|
|
175
|
+
|
|
176
|
+
Returns
|
|
177
|
+
-------
|
|
178
|
+
Callable[[Callable[..., object]], click.Command]
|
|
179
|
+
A decorator which transforms its input into this
|
|
180
|
+
:class:`DefaultGroup`'s default subcommand.
|
|
181
|
+
"""
|
|
61
182
|
default = kwargs.pop("default", False)
|
|
62
|
-
decorator = super().command(
|
|
183
|
+
decorator: Callable[[Callable[..., object]], Command] = super().command(
|
|
184
|
+
*args, **kwargs
|
|
185
|
+
)
|
|
63
186
|
if not default:
|
|
64
187
|
return decorator
|
|
65
188
|
warnings.warn(
|
|
66
189
|
"Use default param of DefaultGroup or set_default_command() instead",
|
|
67
190
|
DeprecationWarning,
|
|
191
|
+
stacklevel=1,
|
|
68
192
|
)
|
|
69
193
|
|
|
70
|
-
def _decorator(f):
|
|
194
|
+
def _decorator(f: Callable) -> Command:
|
|
71
195
|
cmd = decorator(f)
|
|
72
196
|
self.set_default_command(cmd)
|
|
73
197
|
return cmd
|
manim/cli/init/commands.py
CHANGED
|
@@ -10,13 +10,14 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import configparser
|
|
12
12
|
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
13
14
|
|
|
14
15
|
import click
|
|
15
16
|
import cloup
|
|
16
17
|
|
|
17
|
-
from
|
|
18
|
-
from
|
|
19
|
-
from
|
|
18
|
+
from manim._config import console
|
|
19
|
+
from manim.constants import CONTEXT_SETTINGS, EPILOG, QUALITIES
|
|
20
|
+
from manim.utils.file_ops import (
|
|
20
21
|
add_import_statement,
|
|
21
22
|
copy_template_files,
|
|
22
23
|
get_template_names,
|
|
@@ -34,15 +35,15 @@ CFG_DEFAULTS = {
|
|
|
34
35
|
__all__ = ["select_resolution", "update_cfg", "project", "scene"]
|
|
35
36
|
|
|
36
37
|
|
|
37
|
-
def select_resolution():
|
|
38
|
+
def select_resolution() -> tuple[int, int]:
|
|
38
39
|
"""Prompts input of type click.Choice from user. Presents options from QUALITIES constant.
|
|
39
40
|
|
|
40
41
|
Returns
|
|
41
42
|
-------
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
tuple[int, int]
|
|
44
|
+
Tuple containing height and width.
|
|
44
45
|
"""
|
|
45
|
-
resolution_options = []
|
|
46
|
+
resolution_options: list[tuple[int, int]] = []
|
|
46
47
|
for quality in QUALITIES.items():
|
|
47
48
|
resolution_options.append(
|
|
48
49
|
(quality[1]["pixel_height"], quality[1]["pixel_width"]),
|
|
@@ -54,18 +55,21 @@ def select_resolution():
|
|
|
54
55
|
show_default=False,
|
|
55
56
|
default="480p",
|
|
56
57
|
)
|
|
57
|
-
|
|
58
|
+
matches = [res for res in resolution_options if f"{res[0]}p" == choice]
|
|
59
|
+
return matches[0]
|
|
58
60
|
|
|
59
61
|
|
|
60
|
-
def update_cfg(cfg_dict: dict, project_cfg_path: Path):
|
|
61
|
-
"""
|
|
62
|
+
def update_cfg(cfg_dict: dict[str, Any], project_cfg_path: Path) -> None:
|
|
63
|
+
"""Update the ``manim.cfg`` file after reading it from the specified
|
|
64
|
+
``project_cfg_path``.
|
|
62
65
|
|
|
63
66
|
Parameters
|
|
64
67
|
----------
|
|
65
68
|
cfg_dict
|
|
66
|
-
|
|
69
|
+
Values used to update ``manim.cfg`` which is found in
|
|
70
|
+
``project_cfg_path``.
|
|
67
71
|
project_cfg_path
|
|
68
|
-
Path of manim.cfg file.
|
|
72
|
+
Path of the ``manim.cfg`` file.
|
|
69
73
|
"""
|
|
70
74
|
config = configparser.ConfigParser()
|
|
71
75
|
config.read(project_cfg_path)
|
|
@@ -85,7 +89,7 @@ def update_cfg(cfg_dict: dict, project_cfg_path: Path):
|
|
|
85
89
|
context_settings=CONTEXT_SETTINGS,
|
|
86
90
|
epilog=EPILOG,
|
|
87
91
|
)
|
|
88
|
-
@cloup.argument("project_name", type=Path, required=False)
|
|
92
|
+
@cloup.argument("project_name", type=cloup.Path(path_type=Path), required=False)
|
|
89
93
|
@cloup.option(
|
|
90
94
|
"-d",
|
|
91
95
|
"--default",
|
|
@@ -94,13 +98,14 @@ def update_cfg(cfg_dict: dict, project_cfg_path: Path):
|
|
|
94
98
|
help="Default settings for project creation.",
|
|
95
99
|
nargs=1,
|
|
96
100
|
)
|
|
97
|
-
def project(default_settings, **
|
|
101
|
+
def project(default_settings: bool, **kwargs: Any) -> None:
|
|
98
102
|
"""Creates a new project.
|
|
99
103
|
|
|
100
104
|
PROJECT_NAME is the name of the folder in which the new project will be initialized.
|
|
101
105
|
"""
|
|
102
|
-
|
|
103
|
-
|
|
106
|
+
project_name: Path
|
|
107
|
+
if kwargs["project_name"]:
|
|
108
|
+
project_name = kwargs["project_name"]
|
|
104
109
|
else:
|
|
105
110
|
project_name = click.prompt("Project Name", type=Path)
|
|
106
111
|
|
|
@@ -117,7 +122,7 @@ def project(default_settings, **args):
|
|
|
117
122
|
)
|
|
118
123
|
else:
|
|
119
124
|
project_name.mkdir()
|
|
120
|
-
new_cfg = {}
|
|
125
|
+
new_cfg: dict[str, Any] = {}
|
|
121
126
|
new_cfg_path = Path.resolve(project_name / "manim.cfg")
|
|
122
127
|
|
|
123
128
|
if not default_settings:
|
|
@@ -145,23 +150,23 @@ def project(default_settings, **args):
|
|
|
145
150
|
)
|
|
146
151
|
@cloup.argument("scene_name", type=str, required=True)
|
|
147
152
|
@cloup.argument("file_name", type=str, required=False)
|
|
148
|
-
def scene(**
|
|
153
|
+
def scene(**kwargs: Any) -> None:
|
|
149
154
|
"""Inserts a SCENE to an existing FILE or creates a new FILE.
|
|
150
155
|
|
|
151
156
|
SCENE is the name of the scene that will be inserted.
|
|
152
157
|
|
|
153
158
|
FILE is the name of file in which the SCENE will be inserted.
|
|
154
159
|
"""
|
|
155
|
-
template_name = click.prompt(
|
|
160
|
+
template_name: str = click.prompt(
|
|
156
161
|
"template",
|
|
157
162
|
type=click.Choice(get_template_names(), False),
|
|
158
163
|
default="Default",
|
|
159
164
|
)
|
|
160
165
|
scene = (get_template_path() / f"{template_name}.mtp").resolve().read_text()
|
|
161
|
-
scene = scene.replace(template_name + "Template",
|
|
166
|
+
scene = scene.replace(template_name + "Template", kwargs["scene_name"], 1)
|
|
162
167
|
|
|
163
|
-
if
|
|
164
|
-
file_name = Path(
|
|
168
|
+
if kwargs["file_name"]:
|
|
169
|
+
file_name = Path(kwargs["file_name"])
|
|
165
170
|
|
|
166
171
|
if file_name.suffix != ".py":
|
|
167
172
|
file_name = file_name.with_suffix(file_name.suffix + ".py")
|
|
@@ -190,7 +195,7 @@ def scene(**args):
|
|
|
190
195
|
help="Create a new project or insert a new scene.",
|
|
191
196
|
)
|
|
192
197
|
@cloup.pass_context
|
|
193
|
-
def init(ctx):
|
|
198
|
+
def init(ctx: cloup.Context) -> None:
|
|
194
199
|
pass
|
|
195
200
|
|
|
196
201
|
|
manim/cli/plugins/commands.py
CHANGED
|
@@ -10,8 +10,8 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import cloup
|
|
12
12
|
|
|
13
|
-
from
|
|
14
|
-
from
|
|
13
|
+
from manim.constants import CONTEXT_SETTINGS, EPILOG
|
|
14
|
+
from manim.plugins.plugins_flags import list_plugins
|
|
15
15
|
|
|
16
16
|
__all__ = ["plugins"]
|
|
17
17
|
|
|
@@ -29,6 +29,16 @@ __all__ = ["plugins"]
|
|
|
29
29
|
is_flag=True,
|
|
30
30
|
help="List available plugins.",
|
|
31
31
|
)
|
|
32
|
-
def plugins(list_available):
|
|
32
|
+
def plugins(list_available: bool) -> None:
|
|
33
|
+
"""Print a list of all available plugins when calling ``manim plugins -l``
|
|
34
|
+
or ``manim plugins --list``.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
list_available
|
|
39
|
+
If the ``-l`` or ``-list`` option is passed to ``manim plugins``, this
|
|
40
|
+
parameter will be set to ``True``, which will print a list of all
|
|
41
|
+
available plugins.
|
|
42
|
+
"""
|
|
33
43
|
if list_available:
|
|
34
44
|
list_plugins()
|
manim/cli/render/commands.py
CHANGED
|
@@ -13,78 +13,83 @@ import json
|
|
|
13
13
|
import sys
|
|
14
14
|
import urllib.error
|
|
15
15
|
import urllib.request
|
|
16
|
+
from argparse import Namespace
|
|
16
17
|
from pathlib import Path
|
|
17
|
-
from typing import cast
|
|
18
|
+
from typing import Any, cast
|
|
18
19
|
|
|
19
20
|
import cloup
|
|
20
21
|
|
|
21
|
-
from
|
|
22
|
-
from
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
from manim import __version__
|
|
23
|
+
from manim._config import (
|
|
24
|
+
config,
|
|
25
|
+
console,
|
|
26
|
+
error_console,
|
|
27
|
+
logger,
|
|
28
|
+
tempconfig,
|
|
29
|
+
)
|
|
30
|
+
from manim.cli.render.ease_of_access_options import ease_of_access_options
|
|
31
|
+
from manim.cli.render.global_options import global_options
|
|
32
|
+
from manim.cli.render.output_options import output_options
|
|
33
|
+
from manim.cli.render.render_options import render_options
|
|
34
|
+
from manim.constants import EPILOG, RendererType
|
|
35
|
+
from manim.utils.module_ops import scene_classes_from_file
|
|
29
36
|
|
|
30
37
|
__all__ = ["render"]
|
|
31
38
|
|
|
32
39
|
|
|
40
|
+
class ClickArgs(Namespace):
|
|
41
|
+
def __init__(self, args: dict[str, Any]) -> None:
|
|
42
|
+
for name in args:
|
|
43
|
+
setattr(self, name, args[name])
|
|
44
|
+
|
|
45
|
+
def _get_kwargs(self) -> list[tuple[str, Any]]:
|
|
46
|
+
return list(self.__dict__.items())
|
|
47
|
+
|
|
48
|
+
def __eq__(self, other: object) -> bool:
|
|
49
|
+
if not isinstance(other, ClickArgs):
|
|
50
|
+
return NotImplemented
|
|
51
|
+
return vars(self) == vars(other)
|
|
52
|
+
|
|
53
|
+
def __contains__(self, key: str) -> bool:
|
|
54
|
+
return key in self.__dict__
|
|
55
|
+
|
|
56
|
+
def __repr__(self) -> str:
|
|
57
|
+
return str(self.__dict__)
|
|
58
|
+
|
|
59
|
+
|
|
33
60
|
@cloup.command(
|
|
34
61
|
context_settings=None,
|
|
35
62
|
no_args_is_help=True,
|
|
36
63
|
epilog=EPILOG,
|
|
37
64
|
)
|
|
38
|
-
@cloup.argument("file", type=Path, required=True)
|
|
65
|
+
@cloup.argument("file", type=cloup.Path(path_type=Path), required=True)
|
|
39
66
|
@cloup.argument("scene_names", required=False, nargs=-1)
|
|
40
67
|
@global_options
|
|
41
68
|
@output_options
|
|
42
|
-
@render_options
|
|
69
|
+
@render_options
|
|
43
70
|
@ease_of_access_options
|
|
44
|
-
def render(
|
|
45
|
-
**args,
|
|
46
|
-
):
|
|
71
|
+
def render(**kwargs: Any) -> ClickArgs | dict[str, Any]:
|
|
47
72
|
"""Render SCENE(S) from the input FILE.
|
|
48
73
|
|
|
49
74
|
FILE is the file path of the script or a config file.
|
|
50
75
|
|
|
51
76
|
SCENES is an optional list of scenes in the file.
|
|
52
77
|
"""
|
|
53
|
-
|
|
54
|
-
if args["save_as_gif"]:
|
|
78
|
+
if kwargs["save_as_gif"]:
|
|
55
79
|
logger.warning("--save_as_gif is deprecated, please use --format=gif instead!")
|
|
56
|
-
|
|
80
|
+
kwargs["format"] = "gif"
|
|
57
81
|
|
|
58
|
-
if
|
|
82
|
+
if kwargs["save_pngs"]:
|
|
59
83
|
logger.warning("--save_pngs is deprecated, please use --format=png instead!")
|
|
60
|
-
|
|
84
|
+
kwargs["format"] = "png"
|
|
61
85
|
|
|
62
|
-
if
|
|
86
|
+
if kwargs["show_in_file_browser"]:
|
|
63
87
|
logger.warning(
|
|
64
88
|
"The short form of show_in_file_browser is deprecated and will be moved to support --format.",
|
|
65
89
|
)
|
|
66
90
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
for name in args:
|
|
70
|
-
setattr(self, name, args[name])
|
|
71
|
-
|
|
72
|
-
def _get_kwargs(self):
|
|
73
|
-
return list(self.__dict__.items())
|
|
74
|
-
|
|
75
|
-
def __eq__(self, other):
|
|
76
|
-
if not isinstance(other, ClickArgs):
|
|
77
|
-
return NotImplemented
|
|
78
|
-
return vars(self) == vars(other)
|
|
79
|
-
|
|
80
|
-
def __contains__(self, key):
|
|
81
|
-
return key in self.__dict__
|
|
82
|
-
|
|
83
|
-
def __repr__(self):
|
|
84
|
-
return str(self.__dict__)
|
|
85
|
-
|
|
86
|
-
click_args = ClickArgs(args)
|
|
87
|
-
if args["jupyter"]:
|
|
91
|
+
click_args = ClickArgs(kwargs)
|
|
92
|
+
if kwargs["jupyter"]:
|
|
88
93
|
return click_args
|
|
89
94
|
|
|
90
95
|
config.digest_args(click_args)
|
|
@@ -153,4 +158,4 @@ def render(
|
|
|
153
158
|
"You should consider upgrading via [yellow]pip install -U manim[/yellow]",
|
|
154
159
|
)
|
|
155
160
|
|
|
156
|
-
return
|
|
161
|
+
return kwargs
|
|
@@ -1,22 +1,56 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import re
|
|
5
|
+
import sys
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
4
7
|
|
|
5
8
|
from cloup import Choice, option, option_group
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from click import Context, Option
|
|
8
12
|
|
|
9
13
|
__all__ = ["global_options"]
|
|
10
14
|
|
|
15
|
+
logger = logging.getLogger("manim")
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
|
|
18
|
+
def validate_gui_location(
|
|
19
|
+
ctx: Context, param: Option, value: str | None
|
|
20
|
+
) -> tuple[int, int] | None:
|
|
21
|
+
"""If the ``value`` string is given, extract from it the GUI location,
|
|
22
|
+
which should be in any of these formats: 'x;y', 'x,y' or 'x-y'.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
ctx
|
|
27
|
+
The Click context.
|
|
28
|
+
param
|
|
29
|
+
A Click option.
|
|
30
|
+
value
|
|
31
|
+
The optional string which will be parsed.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
tuple[int, int] | None
|
|
36
|
+
If ``value`` is ``None``, the return value is ``None``. Otherwise, it's
|
|
37
|
+
the ``(x, y)`` location for the GUI.
|
|
38
|
+
|
|
39
|
+
Raises
|
|
40
|
+
------
|
|
41
|
+
ValueError
|
|
42
|
+
If ``value`` has an invalid format.
|
|
43
|
+
"""
|
|
44
|
+
if value is None:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
x_offset, y_offset = map(int, re.split(r"[;,\-]", value))
|
|
49
|
+
except Exception:
|
|
50
|
+
logger.error("GUI location option is invalid.")
|
|
51
|
+
sys.exit()
|
|
52
|
+
|
|
53
|
+
return (x_offset, y_offset)
|
|
20
54
|
|
|
21
55
|
|
|
22
56
|
global_options = option_group(
|