Skip to content

Commit

Permalink
Merge branch 'main' into add-docstrings-to-brace
Browse files Browse the repository at this point in the history
  • Loading branch information
behackl authored Jun 30, 2024
2 parents 6321e74 + 8d70b0e commit d604164
Show file tree
Hide file tree
Showing 57 changed files with 1,470 additions and 619 deletions.
10 changes: 8 additions & 2 deletions .github/manimdependency.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
"standalone",
"preview",
"doublestroke",
"ms",
"count1to",
"multitoc",
"prelim2e",
"ragged2e",
"everysel",
"setspace",
"rsfs",
Expand All @@ -29,7 +32,10 @@
"standalone",
"preview",
"doublestroke",
"ms",
"count1to",
"multitoc",
"prelim2e",
"ragged2e",
"everysel",
"setspace",
"rsfs",
Expand Down
25 changes: 6 additions & 19 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,22 @@ repos:
- id: end-of-file-fixer
- id: check-toml
name: Validate Poetry
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
name: isort (python)
- id: isort
name: isort (cython)
types: [cython]
- id: isort
name: isort (pyi)
types: [pyi]
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
hooks:
- id: pyupgrade
name: Update code to new python versions
args: [--py39-plus]
- repo: https://github.com/pre-commit/pygrep-hooks
rev: v1.10.0
hooks:
- id: python-check-blanket-noqa
name: Precision flake ignores
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.5
rev: v0.4.10
hooks:
- id: ruff
name: ruff lint
types: [python]
args: [--exit-non-zero-on-fix]
- id: ruff-format
types: [python]
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
rev: 7.1.0
hooks:
- id: flake8
additional_dependencies:
Expand Down
6 changes: 0 additions & 6 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@

from __future__ import annotations

try:
# https://github.com/moderngl/moderngl/issues/517
import readline # required to prevent a segfault on Python 3.10
except ModuleNotFoundError: # windows
pass

import cairo
import moderngl

Expand Down
4 changes: 2 additions & 2 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ RUN wget -O /tmp/install-tl-unx.tar.gz http://mirror.ctan.org/systems/texlive/tl
tar -xzf /tmp/install-tl-unx.tar.gz -C /tmp/install-tl --strip-components=1 && \
/tmp/install-tl/install-tl --profile=/tmp/texlive-profile.txt \
&& tlmgr install \
amsmath babel-english cbfonts-fd cm-super ctex doublestroke dvisvgm everysel \
amsmath babel-english cbfonts-fd cm-super count1to ctex doublestroke dvisvgm everysel \
fontspec frcursive fundus-calligra gnu-freefont jknapltx latex-bin \
mathastext microtype ms physics preview ragged2e relsize rsfs \
mathastext microtype multitoc physics prelim2e preview ragged2e relsize rsfs \
setspace standalone tipa wasy wasysym xcolor xetex xkeyval

# clone and build manim
Expand Down
1 change: 1 addition & 0 deletions docs/source/contributing/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ Index
docs/examples
docs/references
docs/typings
docs/types
134 changes: 134 additions & 0 deletions docs/source/contributing/docs/types.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
===================
Choosing Type Hints
===================
In order to provide the best user experience,
it's important that type hints are chosen correctly.
With the large variety of types provided by Manim, choosing
which one to use can be difficult. This guide aims to
aid you in the process of choosing the right type for the scenario.


The first step is figuring out which category your type hint fits into.

Coordinates
-----------
Coordinates encompass two main categories: points, and vectors.


Points
~~~~~~
The purpose of points is pretty straightforward: they represent a point
in space. For example:

.. code-block:: python
def status2D(coord: Point2D) -> None:
x, y = coord
print(f"Point at {x=},{y=}")
def status3D(coord: Point3D) -> None:
x, y, z = coord
print(f"Point at {x=},{y=},{z=}")
def get_statuses(coords: Point2D_Array | Point3D_Array) -> None:
for coord in coords:
if len(coord) == 2:
# it's a Point2D
status2D(coord)
else:
# it's a point3D
status3D(coord)
It's important to realize that the status functions accepted both
tuples/lists of the correct length, and ``NDArray``'s of the correct shape.
If they only accepted ``NDArray``'s, we would use their ``Internal`` counterparts:
:class:`~.typing.InternalPoint2D`, :class:`~.typing.InternalPoint3D`, :class:`~.typing.InternalPoint2D_Array` and :class:`~.typing.InternalPoint3D_Array`.

In general, the type aliases prefixed with ``Internal`` should never be used on
user-facing classes and functions, but should be reserved for internal behavior.

Vectors
~~~~~~~
Vectors share many similarities to points. However, they have a different
connotation. Vectors should be used to represent direction. For example,
consider this slightly contrived function:

.. code-block:: python
def shift_mobject(mob: Mobject, direction: Vector3D, scale_factor: float = 1) -> mob:
return mob.shift(direction * scale_factor)
Here we see an important example of the difference. ``direction`` can not, and
should not, be typed as a :class:`~.typing.Point3D` because the function does not accept tuples/lists,
like ``direction=(0, 1, 0)``. You could type it as :class:`~.typing.InternalPoint3D` and
the type checker and linter would be happy; however, this makes the code harder
to understand.

As a general rule, if a parameter is called ``direction`` or ``axis``,
it should be type hinted as some form of :class:`~.VectorND`.

.. warning::

This is not always true. For example, as of Manim 0.18.0, the direction
parameter of the :class:`.Vector` Mobject should be ``Point2D | Point3D``,
as it can also accept ``tuple[float, float]`` and ``tuple[float, float, float]``.

Colors
------
The interface Manim provides for working with colors is :class:`.ManimColor`.
The main color types Manim supports are RGB, RGBA, and HSV. You will want
to add type hints to a function depending on which type it uses. If any color will work,
you will need something like:

.. code-block:: python
if TYPE_CHECKING:
from manim.utils.color import ParsableManimColor
# type hint stuff with ParsableManimColor
Béziers
-------
Manim internally represents a :class:`.Mobject` by a collection of points. In the case of :class:`.VMobject`,
the most commonly used subclass of :class:`.Mobject`, these points represent Bézier curves,
which are a way of representing a curve using a sequence of points.

.. note::

To learn more about Béziers, take a look at https://pomax.github.io/bezierinfo/


Manim supports two different renderers, which each have different representations of
Béziers: Cairo uses cubic Bézier curves, while OpenGL uses quadratic Bézier curves.

Type hints like :class:`~.typing.BezierPoints` represent a single bezier curve, and :class:`~.typing.BezierPath`
represents multiple Bézier curves. A :class:`~.typing.Spline` is when the Bézier curves in a :class:`~.typing.BezierPath`
forms a single connected curve. Manim also provides more specific type aliases when working with
quadratic or cubic curves, and they are prefixed with their respective type (e.g. :class:`~.typing.CubicBezierPoints`,
is a :class:`~.typing.BezierPoints` consisting of exactly 4 points representing a cubic Bézier curve).


Functions
---------
Throughout the codebase, many different types of functions are used. The most obvious example
is a rate function, which takes in a float and outputs a float (``Callable[[float], float]``).
Another example is for overriding animations. One will often need to map a :class:`.Mobject`
to an overridden :class:`.Animation`, and for that we have the :class:`~.typing.FunctionOverride` type hint.

:class:`~.typing.PathFuncType` and :class:`~.typing.MappingFunction` are more niche, but are related to moving objects
along a path, or applying functions. If you need to use it, you'll know.


Images
------
There are several representations of images in Manim. The most common is
the representation as a NumPy array of floats representing the pixels of an image.
This is especially common when it comes to the OpenGL renderer.

This is the use case of the :class:`~.typing.Image` type hint. Sometimes, Manim may use ``PIL.Image``,
in which case one should use that type hint instead.
Of course, if a more specific type of image is needed, it can be annotated as such.
6 changes: 3 additions & 3 deletions docs/source/contributing/docs/typings.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
==============
Adding Typings
==============
==================
Typing Conventions
==================

.. warning::
This section is still a work in progress.
Expand Down
1 change: 0 additions & 1 deletion manim/animation/indication.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ def construct(self):
from ..mobject.types.vectorized_mobject import VGroup, VMobject
from ..utils.bezier import interpolate, inverse_interpolate
from ..utils.color import GREY, YELLOW, ParsableManimColor
from ..utils.deprecation import deprecated
from ..utils.rate_functions import smooth, there_and_back, wiggle
from ..utils.space_ops import normalize

Expand Down
3 changes: 0 additions & 3 deletions manim/cli/checkhealth/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@

import os
import shutil
import subprocess
from typing import Callable

from ..._config import config

__all__ = ["HEALTH_CHECKS"]

HEALTH_CHECKS = []
Expand Down
2 changes: 2 additions & 0 deletions manim/cli/default_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
of ``click.Group``.
"""

from __future__ import annotations

import warnings

import cloup
Expand Down
2 changes: 1 addition & 1 deletion manim/mobject/geometry/boolean_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def _convert_skia_path_to_vmobject(self, path: SkiaPath) -> VMobject:
n1, n2 = self._convert_2d_to_3d_array(points)
vmobject.add_quadratic_bezier_curve_to(n1, n2)
else:
raise Exception("Unsupported: %s" % path_verb)
raise Exception(f"Unsupported: {path_verb}")
return vmobject


Expand Down
30 changes: 24 additions & 6 deletions manim/mobject/geometry/line.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@
class Line(TipableVMobject):
def __init__(
self,
start: Point3D = LEFT,
end: Point3D = RIGHT,
start: Point3D | Mobject = LEFT,
end: Point3D | Mobject = RIGHT,
buff: float = 0,
path_arc: float | None = None,
**kwargs,
Expand All @@ -63,16 +63,32 @@ def generate_points(self) -> None:

def set_points_by_ends(
self,
start: Point3D,
end: Point3D,
start: Point3D | Mobject,
end: Point3D | Mobject,
buff: float = 0,
path_arc: float = 0,
) -> None:
"""Sets the points of the line based on its start and end points.
Unlike :meth:`put_start_and_end_on`, this method respects `self.buff` and
Mobject bounding boxes.
Parameters
----------
start
The start point or Mobject of the line.
end
The end point or Mobject of the line.
buff
The empty space between the start and end of the line, by default 0.
path_arc
The angle of a circle spanned by this arc, by default 0 which is a straight line.
"""
self._set_start_and_end_attrs(start, end)
if path_arc:
arc = ArcBetweenPoints(self.start, self.end, angle=self.path_arc)
self.set_points(arc.points)
else:
self.set_points_as_corners([start, end])
self.set_points_as_corners([self.start, self.end])

self._account_for_buff(buff)

Expand All @@ -93,7 +109,9 @@ def _account_for_buff(self, buff: float) -> Self:
self.pointwise_become_partial(self, buff_proportion, 1 - buff_proportion)
return self

def _set_start_and_end_attrs(self, start: Point3D, end: Point3D) -> None:
def _set_start_and_end_attrs(
self, start: Point3D | Mobject, end: Point3D | Mobject
) -> None:
# If either start or end are Mobjects, this
# gives their centers
rough_start = self._pointify(start)
Expand Down
22 changes: 17 additions & 5 deletions manim/mobject/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
if TYPE_CHECKING:
from typing_extensions import TypeAlias

from manim.scene.scene import Scene
from manim.typing import Point3D

NxGraph: TypeAlias = nx.classes.graph.Graph | nx.classes.digraph.DiGraph
Expand Down Expand Up @@ -477,7 +478,7 @@ def _determine_graph_layout(
return cast(LayoutFunction, layout)(
nx_graph, scale=layout_scale, **layout_config
)
except TypeError as e:
except TypeError:
raise ValueError(
f"The layout '{layout}' is neither a recognized layout, a layout function,"
"nor a vertex placement dictionary.",
Expand Down Expand Up @@ -560,6 +561,7 @@ class GenericGraph(VMobject, metaclass=ConvertToOpenGL):
all other configuration options for a vertex.
edge_type
The mobject class used for displaying edges in the scene.
Must be a subclass of :class:`~.Line` for default updaters to work.
edge_config
Either a dictionary containing keyword arguments to be passed
to the class specified via ``edge_type``, or a dictionary whose
Expand Down Expand Up @@ -1558,7 +1560,12 @@ def _populate_edge_dict(
def update_edges(self, graph):
for (u, v), edge in graph.edges.items():
# Undirected graph has a Line edge
edge.put_start_and_end_on(graph[u].get_center(), graph[v].get_center())
edge.set_points_by_ends(
graph[u].get_center(),
graph[v].get_center(),
buff=self._edge_config.get("buff", 0),
path_arc=self._edge_config.get("path_arc", 0),
)

def __repr__(self: Graph) -> str:
return f"Undirected graph on {len(self.vertices)} vertices and {len(self.edges)} edges"
Expand Down Expand Up @@ -1767,10 +1774,15 @@ def update_edges(self, graph):
deformed.
"""
for (u, v), edge in graph.edges.items():
edge_type = type(edge)
tip = edge.pop_tips()[0]
new_edge = edge_type(self[u], self[v], **self._edge_config[(u, v)])
edge.become(new_edge)
# Passing the Mobject instead of the vertex makes the tip
# stop on the bounding box of the vertex.
edge.set_points_by_ends(
graph[u],
graph[v],
buff=self._edge_config.get("buff", 0),
path_arc=self._edge_config.get("path_arc", 0),
)
edge.add_tip(tip)

def __repr__(self: DiGraph) -> str:
Expand Down
Loading

0 comments on commit d604164

Please sign in to comment.