From 4791fc072c7c92cd66dc793a608f566a3c3f1289 Mon Sep 17 00:00:00 2001 From: Taksh Date: Tue, 30 Jun 2026 11:57:22 +0530 Subject: [PATCH 1/2] docs(unit): document unit conversion helpers Fixes #2512. Adds module docs, building-blocks tutorial section, API reference entry, and validates Percent axis input. Co-authored-by: Cursor --- .../source/reference_index/utilities_misc.rst | 1 + docs/source/tutorials/building_blocks.rst | 13 ++++++ manim/utils/unit.py | 44 +++++++++++++++++-- tests/module/utils/test_units.py | 3 ++ 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/docs/source/reference_index/utilities_misc.rst b/docs/source/reference_index/utilities_misc.rst index bda1cf4961..f6877bcff4 100644 --- a/docs/source/reference_index/utilities_misc.rst +++ b/docs/source/reference_index/utilities_misc.rst @@ -32,4 +32,5 @@ Module Index ~utils.tex ~utils.tex_file_writing ~utils.tex_templates + ~utils.unit typing diff --git a/docs/source/tutorials/building_blocks.rst b/docs/source/tutorials/building_blocks.rst index 46d6ea278f..0360e0e796 100644 --- a/docs/source/tutorials/building_blocks.rst +++ b/docs/source/tutorials/building_blocks.rst @@ -134,6 +134,19 @@ measuring units but as a way to determine the border to use for alignment. The coordinates of the borders of a mobject are determined using an imaginary bounding box around it. +Manim also provides helpers in :mod:`~.utils.unit` for converting between +common units and scene coordinates: + +.. code-block:: python + + from manim import unit, X_AXIS + + pixel_width = 50 * unit.Pixels + angle_radians = 90 * unit.Degrees + tenth_of_frame = unit.Percent(X_AXIS) * 10 + +See :mod:`~.utils.unit` for details. + .. tip:: Many methods in manim can be chained together. For example the two lines diff --git a/manim/utils/unit.py b/manim/utils/unit.py index c4755f0191..37c7ee0240 100644 --- a/manim/utils/unit.py +++ b/manim/utils/unit.py @@ -1,4 +1,17 @@ -"""Implement the Unit class.""" +"""Unit conversion helpers for Manim coordinates. + +Manim scenes use abstract *Munits* (Manim units) for positioning mobjects. +These helpers convert pixels, degrees, and screen percentages into Munits. + +Examples +-------- +.. code-block:: pycon + + >>> from manim import unit, X_AXIS + >>> 50 * unit.Pixels # 50 pixels -> Munits + >>> 90 * unit.Degrees # degrees -> radians + >>> unit.Percent(X_AXIS) * 10 # 10% of frame width +""" from __future__ import annotations @@ -11,6 +24,8 @@ class _PixelUnits: + """Convert pixel counts to Munits using the current frame width.""" + def __mul__(self, val: float) -> float: return val * config.frame_width / config.pixel_width @@ -19,13 +34,31 @@ def __rmul__(self, val: float) -> float: class Percent: + """Convert a percentage of the frame width or height to Munits. + + Parameters + ---------- + axis + Either ``X_AXIS`` or ``Y_AXIS``. ``Z_AXIS`` is not supported. + + Examples + -------- + .. code-block:: pycon + + >>> from manim import unit, X_AXIS, Y_AXIS + >>> unit.Percent(X_AXIS) * 10 # 10% of frame width + >>> unit.Percent(Y_AXIS) * 25 # 25% of frame height + """ + def __init__(self, axis: Vector3D) -> None: if np.array_equal(axis, constants.X_AXIS): self.length = config.frame_width - if np.array_equal(axis, constants.Y_AXIS): + elif np.array_equal(axis, constants.Y_AXIS): self.length = config.frame_height - if np.array_equal(axis, constants.Z_AXIS): + elif np.array_equal(axis, constants.Z_AXIS): raise NotImplementedError("length of Z axis is undefined") + else: + raise ValueError("Percent axis must be X_AXIS or Y_AXIS.") def __mul__(self, val: float) -> float: return val / 100 * self.length @@ -35,5 +68,10 @@ def __rmul__(self, val: float) -> float: Pixels = _PixelUnits() +"""Convert pixel counts to Munits: ``50 * Pixels``.""" + Degrees = constants.PI / 180 +"""Convert degrees to radians: ``90 * Degrees``.""" + Munits = 1 +"""Identity unit for Manim coordinates.""" diff --git a/tests/module/utils/test_units.py b/tests/module/utils/test_units.py index 5f2995e626..13c9a399c1 100644 --- a/tests/module/utils/test_units.py +++ b/tests/module/utils/test_units.py @@ -27,5 +27,8 @@ def test_units(config): with pytest.raises(NotImplementedError): Percent(Z_AXIS) + with pytest.raises(ValueError, match="X_AXIS or Y_AXIS"): + Percent(np.array([1, 1, 0])) + # Degrees should convert from degrees to radians np.testing.assert_allclose(180 * Degrees, PI) From e2a71acaa886dcfc399ce231ef6ee05708dcaf9c Mon Sep 17 00:00:00 2001 From: Taksh Date: Tue, 30 Jun 2026 12:10:47 +0530 Subject: [PATCH 2/2] fix(docs): add expected output to unit module doctests CI doctest runner requires explicit expected values for pycon examples. Co-authored-by: Cursor --- manim/utils/unit.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/manim/utils/unit.py b/manim/utils/unit.py index 37c7ee0240..a524cd4bc1 100644 --- a/manim/utils/unit.py +++ b/manim/utils/unit.py @@ -9,8 +9,11 @@ >>> from manim import unit, X_AXIS >>> 50 * unit.Pixels # 50 pixels -> Munits + 0.37037037037037035 >>> 90 * unit.Degrees # degrees -> radians + 1.5707963267948966 >>> unit.Percent(X_AXIS) * 10 # 10% of frame width + 1.4222222222222223 """ from __future__ import annotations @@ -47,7 +50,9 @@ class Percent: >>> from manim import unit, X_AXIS, Y_AXIS >>> unit.Percent(X_AXIS) * 10 # 10% of frame width + 1.4222222222222223 >>> unit.Percent(Y_AXIS) * 25 # 25% of frame height + 2.0 """ def __init__(self, axis: Vector3D) -> None: