Skip to content

Commit 6bba55e

Browse files
authored
Merge branch 'main' into fix-getarea-arguments
2 parents c4e52f2 + 325fa08 commit 6bba55e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1337
-261
lines changed

.github/codeql.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ query-filters:
99
id: py/multiple-calls-to-init
1010
- exclude:
1111
id: py/missing-call-to-init
12+
- exclude:
13+
id: py/method-first-arg-is-not-self
1214
paths:
1315
- manim
1416
paths-ignore:

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ repos:
1313
- id: check-toml
1414
name: Validate pyproject.toml
1515
- repo: https://github.com/astral-sh/ruff-pre-commit
16-
rev: v0.9.3
16+
rev: v0.11.0
1717
hooks:
1818
- id: ruff
1919
name: ruff lint
@@ -22,7 +22,7 @@ repos:
2222
- id: ruff-format
2323
types: [python]
2424
- repo: https://github.com/pre-commit/mirrors-mypy
25-
rev: v1.14.1
25+
rev: v1.15.0
2626
hooks:
2727
- id: mypy
2828
additional_dependencies:
@@ -36,7 +36,7 @@ repos:
3636
files: ^manim/
3737

3838
- repo: https://github.com/codespell-project/codespell
39-
rev: v2.4.0
39+
rev: v2.4.1
4040
hooks:
4141
- id: codespell
4242
files: ^.*\.(py|md|rst)$

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ Examples of conflicts of interest include:
152152
* The reporter or reported person is a maintainer who regularly reviews your contributions
153153
* The reporter or reported person is your metamour.
154154
* The reporter or reported person is your family member
155+
155156
Committee members do not need to state why they have a conflict of interest, only that one exists. Other team members should not ask why the person has a conflict of interest.
156157

157158
Anyone who has a conflict of interest will remove themselves from the discussion of the incident, and recluse themselves from voting on a response to the report.

README.md

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,17 @@
2222
Manim is an animation engine for explanatory math videos. It's used to create precise animations programmatically, as demonstrated in the videos of [3Blue1Brown](https://www.3blue1brown.com/).
2323

2424
> [!NOTE]
25-
> The community edition of Manim has been forked from 3b1b/manim, a tool originally created and open-sourced by Grant Sanderson, also creator of the 3Blue1Brown educational math videos. While Grant Sanderson’s repository continues to be maintained separately by him, he is not among the maintainers of the community edition. We recommend this version for its continued development, improved features, enhanced documentation, and more active community-driven maintenance. If you would like to study how Grant makes his videos, head over to his repository ([3b1b/manim](https://github.com/3b1b/manim)).
25+
> The community edition of Manim (ManimCE) is a version maintained and developed by the community. It was forked from 3b1b/manim, a tool originally created and open-sourced by Grant Sanderson, also creator of the 3Blue1Brown educational math videos. While Grant Sanderson continues to maintain his own repository, we recommend this version for its continued development, improved features, enhanced documentation, and more active community-driven maintenance. If you would like to study how Grant makes his videos, head over to his repository ([3b1b/manim](https://github.com/3b1b/manim)).
2626
2727
## Table of Contents:
2828

29-
- [Installation](#installation)
30-
- [Usage](#usage)
31-
- [Documentation](#documentation)
32-
- [Docker](#docker)
33-
- [Help with Manim](#help-with-manim)
34-
- [Contributing](#contributing)
35-
- [License](#license)
29+
- [Installation](#installation)
30+
- [Usage](#usage)
31+
- [Documentation](#documentation)
32+
- [Docker](#docker)
33+
- [Help with Manim](#help-with-manim)
34+
- [Contributing](#contributing)
35+
- [License](#license)
3636

3737
## Installation
3838

@@ -90,9 +90,9 @@ The `-p` flag in the command above is for previewing, meaning the video file wil
9090

9191
Some other useful flags include:
9292

93-
- `-s` to skip to the end and just show the final frame.
94-
- `-n <number>` to skip ahead to the `n`'th animation of a scene.
95-
- `-f` show the file in the file browser.
93+
- `-s` to skip to the end and just show the final frame.
94+
- `-n <number>` to skip ahead to the `n`'th animation of a scene.
95+
- `-f` show the file in the file browser.
9696

9797
For a thorough list of command line arguments, visit the [documentation](https://docs.manim.community/en/stable/guides/configuration.html).
9898

@@ -120,8 +120,8 @@ The contribution guide may become outdated quickly; we highly recommend joining
120120
[Discord server](https://www.manim.community/discord/) to discuss any potential
121121
contributions and keep up to date with the latest developments.
122122

123-
Most developers on the project use `poetry` for management. You'll want to have poetry installed and available in your environment.
124-
Learn more about `poetry` at its [documentation](https://python-poetry.org/docs/) and find out how to install manim with poetry at the [manim dev-installation guide](https://docs.manim.community/en/stable/contributing/development.html) in the manim documentation.
123+
Most developers on the project use `uv` for management. You'll want to have uv installed and available in your environment.
124+
Learn more about `uv` at its [documentation](https://docs.astral.sh/uv/) and find out how to install manim with uv at the [manim dev-installation guide](https://docs.manim.community/en/latest/contributing/development.html) in the manim documentation.
125125

126126
## How to Cite Manim
127127

docs/source/examples.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ Animations
299299
path.become(previous_path)
300300
path.add_updater(update_path)
301301
self.add(path, dot)
302-
self.play(Rotating(dot, radians=PI, about_point=RIGHT, run_time=2))
302+
self.play(Rotating(dot, angle=PI, about_point=RIGHT, run_time=2))
303303
self.wait()
304304
self.play(dot.animate.shift(UP))
305305
self.play(dot.animate.shift(LEFT))

docs/source/guides/using_text.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ For example:
5050
)
5151
self.add(text)
5252

53-
.. _Pango library: https://pango.gnome.org
53+
.. _Pango library: https://pango.org
5454

5555
Working with :class:`~.Text`
5656
============================

docs/source/tutorials/building_blocks.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,13 @@ Generally, you start with the starting number and add only some part of the valu
327327
So, the logic of calculating the number to display at each step will be ``50 + alpha * (100 - 50)``.
328328
Once you set the calculated value for the :class:`~.DecimalNumber`, you are done.
329329

330+
.. note::
331+
332+
If you're creating a custom animation and want to use a ``rate_func``, you must explicitly apply
333+
``self.rate_func(alpha)`` to the parameter you're animating. For example, try switching the rate
334+
function to ``rate_functions.there_and_back`` to observe how it affects the counting behavior.
335+
336+
330337
Once you have defined your ``Count`` animation, you can play it in your :class:`~.Scene` for any duration you want for any :class:`~.DecimalNumber` with any rate function.
331338

332339
.. manim:: CountingScene
@@ -343,7 +350,7 @@ Once you have defined your ``Count`` animation, you can play it in your :class:`
343350
344351
def interpolate_mobject(self, alpha: float) -> None:
345352
# Set value of DecimalNumber according to alpha
346-
value = self.start + (alpha * (self.end - self.start))
353+
value = self.start + (self.rate_func(alpha) * (self.end - self.start))
347354
self.mobject.set_value(value)
348355

349356

manim/_config/cli_colors.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
def parse_cli_ctx(parser: configparser.SectionProxy) -> dict[str, Any]:
12-
formatter_settings: dict[str, str | int] = {
12+
formatter_settings: dict[str, str | int | None] = {
1313
"indent_increment": int(parser["indent_increment"]),
1414
"width": int(parser["width"]),
1515
"col1_max_width": int(parser["col1_max_width"]),
@@ -37,22 +37,24 @@ def parse_cli_ctx(parser: configparser.SectionProxy) -> dict[str, Any]:
3737
if theme is None:
3838
formatter = HelpFormatter.settings(
3939
theme=HelpTheme(**theme_settings),
40-
**formatter_settings, # type: ignore[arg-type]
40+
**formatter_settings,
4141
)
4242
elif theme.lower() == "dark":
4343
formatter = HelpFormatter.settings(
4444
theme=HelpTheme.dark().with_(**theme_settings),
45-
**formatter_settings, # type: ignore[arg-type]
45+
**formatter_settings,
4646
)
4747
elif theme.lower() == "light":
4848
formatter = HelpFormatter.settings(
4949
theme=HelpTheme.light().with_(**theme_settings),
50-
**formatter_settings, # type: ignore[arg-type]
50+
**formatter_settings,
5151
)
5252

53-
return Context.settings(
53+
return_val: dict[str, Any] = Context.settings(
5454
align_option_groups=parser["align_option_groups"].lower() == "true",
5555
align_sections=parser["align_sections"].lower() == "true",
5656
show_constraints=True,
5757
formatter_settings=formatter,
5858
)
59+
60+
return return_val

manim/_config/logger_utils.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import copy
1717
import json
1818
import logging
19-
from typing import TYPE_CHECKING
19+
from typing import TYPE_CHECKING, Any
2020

2121
from rich import color, errors
2222
from rich import print as printf
@@ -91,7 +91,7 @@ def make_logger(
9191
# set the rich handler
9292
rich_handler = RichHandler(
9393
console=console,
94-
show_time=parser.getboolean("log_timestamps"),
94+
show_time=parser.getboolean("log_timestamps", fallback=False),
9595
keywords=HIGHLIGHTED_KEYWORDS,
9696
)
9797

@@ -108,7 +108,7 @@ def make_logger(
108108
return logger, console, error_console
109109

110110

111-
def parse_theme(parser: configparser.SectionProxy) -> Theme:
111+
def parse_theme(parser: configparser.SectionProxy) -> Theme | None:
112112
"""Configure the rich style of logger and console output.
113113
114114
Parameters
@@ -126,7 +126,7 @@ def parse_theme(parser: configparser.SectionProxy) -> Theme:
126126
:func:`make_logger`.
127127
128128
"""
129-
theme = {key.replace("_", "."): parser[key] for key in parser}
129+
theme: dict[str, Any] = {key.replace("_", "."): parser[key] for key in parser}
130130

131131
theme["log.width"] = None if theme["log.width"] == "-1" else int(theme["log.width"])
132132
theme["log.height"] = (
@@ -188,8 +188,11 @@ def format(self, record: logging.LogRecord) -> str:
188188
"""Format the record in a custom JSON format."""
189189
record_c = copy.deepcopy(record)
190190
if record_c.args:
191-
for arg in record_c.args:
192-
record_c.args[arg] = "<>"
191+
if isinstance(record_c.args, dict):
192+
for arg in record_c.args:
193+
record_c.args[arg] = "<>"
194+
else:
195+
record_c.args = ("<>",) * len(record_c.args)
193196
return json.dumps(
194197
{
195198
"levelname": record_c.levelname,

manim/_config/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1448,7 +1448,7 @@ def enable_gui(self, value: bool) -> None:
14481448

14491449
@property
14501450
def gui_location(self) -> tuple[Any]:
1451-
"""Enable GUI interaction."""
1451+
"""Location parameters for the GUI window (e.g., screen coordinates or layout settings)."""
14521452
return self._d["gui_location"]
14531453

14541454
@gui_location.setter

manim/animation/animation.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class Animation:
5454
For example ``rate_func(0.5)`` is the proportion of the animation that is done
5555
after half of the animations run time.
5656
57-
5857
reverse_rate_function
5958
Reverses the rate function of the animation. Setting ``reverse_rate_function``
6059
does not have any effect on ``remover`` or ``introducer``. These need to be
@@ -121,7 +120,7 @@ def __new__(
121120
if func is not None:
122121
anim = func(mobject, *args, **kwargs)
123122
logger.debug(
124-
f"The {cls.__name__} animation has been is overridden for "
123+
f"The {cls.__name__} animation has been overridden for "
125124
f"{type(mobject).__name__} mobjects. use_override = False can "
126125
f" be used as keyword argument to prevent animation overriding.",
127126
)
@@ -141,7 +140,7 @@ def __init__(
141140
introducer: bool = False,
142141
*,
143142
_on_finish: Callable[[], None] = lambda _: None,
144-
**kwargs,
143+
use_override: bool = True, # included here to avoid TypeError if passed from a subclass' constructor
145144
) -> None:
146145
self._typecheck_input(mobject)
147146
self.run_time: float = run_time
@@ -161,8 +160,6 @@ def __init__(
161160
else:
162161
self.starting_mobject: Mobject = Mobject()
163162
self.mobject: Mobject = mobject if mobject is not None else Mobject()
164-
if kwargs:
165-
logger.debug("Animation received extra kwargs: %s", kwargs)
166163

167164
if hasattr(self, "CONFIG"):
168165
logger.error(
@@ -499,6 +496,8 @@ def __init_subclass__(cls, **kwargs) -> None:
499496

500497
cls._original__init__ = cls.__init__
501498

499+
_original__init__ = __init__ # needed if set_default() is called with no kwargs directly from Animation
500+
502501
@classmethod
503502
def set_default(cls, **kwargs) -> None:
504503
"""Sets the default values of keyword arguments.

manim/animation/changing.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
__all__ = ["AnimatedBoundary", "TracedPath"]
66

7+
from collections.abc import Sequence
78
from typing import Callable
89

10+
from typing_extensions import Any, Self
11+
12+
from manim.mobject.mobject import Mobject
913
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
1014
from manim.mobject.types.vectorized_mobject import VGroup, VMobject
1115
from manim.utils.color import (
@@ -16,7 +20,7 @@
1620
WHITE,
1721
ParsableManimColor,
1822
)
19-
from manim.utils.rate_functions import smooth
23+
from manim.utils.rate_functions import RateFunction, smooth
2024

2125

2226
class AnimatedBoundary(VGroup):
@@ -38,14 +42,14 @@ def construct(self):
3842

3943
def __init__(
4044
self,
41-
vmobject,
42-
colors=[BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
43-
max_stroke_width=3,
44-
cycle_rate=0.5,
45-
back_and_forth=True,
46-
draw_rate_func=smooth,
47-
fade_rate_func=smooth,
48-
**kwargs,
45+
vmobject: VMobject,
46+
colors: Sequence[ParsableManimColor] = [BLUE_D, BLUE_B, BLUE_E, GREY_BROWN],
47+
max_stroke_width: float = 3,
48+
cycle_rate: float = 0.5,
49+
back_and_forth: bool = True,
50+
draw_rate_func: RateFunction = smooth,
51+
fade_rate_func: RateFunction = smooth,
52+
**kwargs: Any,
4953
):
5054
super().__init__(**kwargs)
5155
self.colors = colors
@@ -59,10 +63,10 @@ def __init__(
5963
vmobject.copy().set_style(stroke_width=0, fill_opacity=0) for x in range(2)
6064
]
6165
self.add(*self.boundary_copies)
62-
self.total_time = 0
66+
self.total_time = 0.0
6367
self.add_updater(lambda m, dt: self.update_boundary_copies(dt))
6468

65-
def update_boundary_copies(self, dt):
69+
def update_boundary_copies(self, dt: float) -> None:
6670
# Not actual time, but something which passes at
6771
# an altered rate to make the implementation below
6872
# cleaner
@@ -78,9 +82,9 @@ def update_boundary_copies(self, dt):
7882
fade_alpha = self.fade_rate_func(alpha)
7983

8084
if self.back_and_forth and int(time) % 2 == 1:
81-
bounds = (1 - draw_alpha, 1)
85+
bounds = (1.0 - draw_alpha, 1.0)
8286
else:
83-
bounds = (0, draw_alpha)
87+
bounds = (0.0, draw_alpha)
8488
self.full_family_become_partial(growing, vmobject, *bounds)
8589
growing.set_stroke(colors[index], width=msw)
8690

@@ -90,7 +94,9 @@ def update_boundary_copies(self, dt):
9094

9195
self.total_time += dt
9296

93-
def full_family_become_partial(self, mob1, mob2, a, b):
97+
def full_family_become_partial(
98+
self, mob1: VMobject, mob2: VMobject, a: float, b: float
99+
) -> Self:
94100
family1 = mob1.family_members_with_points()
95101
family2 = mob2.family_members_with_points()
96102
for sm1, sm2 in zip(family1, family2):
@@ -146,20 +152,21 @@ def __init__(
146152
stroke_width: float = 2,
147153
stroke_color: ParsableManimColor | None = WHITE,
148154
dissipating_time: float | None = None,
149-
**kwargs,
150-
):
155+
**kwargs: Any,
156+
) -> None:
151157
super().__init__(stroke_color=stroke_color, stroke_width=stroke_width, **kwargs)
152158
self.traced_point_func = traced_point_func
153159
self.dissipating_time = dissipating_time
154-
self.time = 1 if self.dissipating_time else None
160+
self.time = 1.0 if self.dissipating_time else None
155161
self.add_updater(self.update_path)
156162

157-
def update_path(self, mob, dt):
163+
def update_path(self, mob: Mobject, dt: float) -> None:
158164
new_point = self.traced_point_func()
159165
if not self.has_points():
160166
self.start_new_path(new_point)
161167
self.add_line_to(new_point)
162168
if self.dissipating_time:
169+
assert self.time is not None
163170
self.time += dt
164171
if self.time - 1 > self.dissipating_time:
165172
nppcc = self.n_points_per_curve

0 commit comments

Comments
 (0)