Skip to content

Introduce seed in random_color method to produce colors deterministically #4265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
96 changes: 94 additions & 2 deletions manim/utils/color/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,9 +1508,100 @@
ManimColor
A random :class:`ManimColor`.
"""
import manim.utils.color.manim_colors as manim_colors
return RandomColorGenerator().next()

return random.choice(manim_colors._all_manim_colors)

class RandomColorGenerator:
"""
A generator for producing random colors from a given list of Manim colors,
Comment on lines +1515 to +1516
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
A generator for producing random colors from a given list of Manim colors,
"""A generator for producing random colors from a given list of Manim colors,

optionally in a reproducible sequence using a seed value.

When initialized with a specific seed, this class produces a deterministic
sequence of `ManimColor` instances. If no seed is provided, the selection is
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sequence of `ManimColor` instances. If no seed is provided, the selection is
sequence of :class:`.ManimColor` instances. If no seed is provided, the selection is

non-deterministic using Python’s global random state.

Parameters
----------
seed : int | None, optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type annoations are taken from the actual type hints, no need to add them twice

Suggested change
seed : int | None, optional
seed

A seed value to initialize the internal random number generator.
If None (default), colors are chosen using the global random state.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If None (default), colors are chosen using the global random state.
If ``None`` (the default), colors are chosen using the global random state.


sample_colors : list[ManimColor], optional
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sample_colors : list[ManimColor], optional
sample_colors

A custom list of Manim colors to sample from. Defaults to the full Manim color palette.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A custom list of Manim colors to sample from. Defaults to the full Manim color palette.
A custom list of Manim colors to sample from. Defaults to the full Manim
color palette.


Examples
--------
Without a seed (non-deterministic)::

>>> from manim import RandomColorGenerator, ManimColor, RED, GREEN, BLUE
>>> rnd = RandomColorGenerator()
>>> isinstance(rnd.next(), ManimColor)
True

With a seed (deterministic sequence)::

>>> rnd = RandomColorGenerator(42)
>>> rnd.next()
ManimColor('#ECE7E2')
>>> rnd.next()
ManimColor('#BBBBBB')
>>> rnd.next()
ManimColor('#BBBBBB')

Re-initializing with the same seed gives the same sequence::

>>> rnd2 = RandomColorGenerator(42)
>>> rnd2.next()
ManimColor('#ECE7E2')
>>> rnd2.next()
ManimColor('#BBBBBB')
>>> rnd2.next()
ManimColor('#BBBBBB')

Using a custom color list::

>>> custom_palette = [RED, GREEN, BLUE]
>>> rnd_custom = RandomColorGenerator(1, sample_colors=custom_palette)
>>> rnd_custom.next() in custom_palette
True
>>> rnd_custom.next() in custom_palette
True

Without a seed and custom palette (non-deterministic)::

>>> rnd_nodet = RandomColorGenerator(sample_colors=[RED])
>>> rnd_nodet.next()
ManimColor('#FC6255')
"""

def __init__(
self,
seed: int | None = None,
sample_colors: list[ManimColor] | None = None,
) -> None:
self.choice = random.choice if seed is None else random.Random(seed).choice

from manim.utils.color.manim_colors import _all_manim_colors

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
manim.utils.color.manim_colors
begins an import cycle.

self.colors = _all_manim_colors if sample_colors is None else sample_colors

def next(self) -> ManimColor:
"""
Returns the next color from the configured color list.
Comment on lines +1589 to +1590
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""
Returns the next color from the configured color list.
"""Return the next color from the configured color list.


Returns
-------
ManimColor
A randomly selected color from the specified color list.

Examples
--------
>>> from manim import RandomColorGenerator, RED
>>> rnd = RandomColorGenerator(sample_colors=[RED])
>>> rnd.next()
ManimColor('#FC6255')
"""
return self.choice(self.colors)


def get_shaded_rgb(
Expand Down Expand Up @@ -1566,6 +1657,7 @@
"average_color",
"random_bright_color",
"random_color",
"RandomColorGenerator",
"get_shaded_rgb",
"HSV",
"RGBA",
Expand Down