Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/reference_index/utilities_misc.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ Module Index
~utils.tex
~utils.tex_file_writing
~utils.tex_templates
~utils.unit
typing
13 changes: 13 additions & 0 deletions docs/source/tutorials/building_blocks.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
49 changes: 46 additions & 3 deletions manim/utils/unit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,20 @@
"""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
0.37037037037037035
>>> 90 * unit.Degrees # degrees -> radians
1.5707963267948966
>>> unit.Percent(X_AXIS) * 10 # 10% of frame width
1.4222222222222223
"""

from __future__ import annotations

Expand All @@ -11,6 +27,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

Expand All @@ -19,13 +37,33 @@ 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
1.4222222222222223
>>> unit.Percent(Y_AXIS) * 25 # 25% of frame height
2.0
"""

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
Expand All @@ -35,5 +73,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."""
3 changes: 3 additions & 0 deletions tests/module/utils/test_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Loading