Skip to content

Commit c9707f2

Browse files
ishu9bansalpre-commit-ci[bot]behackl
authored
Introduce seed in random_color method to produce colors deterministically (#4265)
* add a random generator to hold the seed for random generation * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add DOCSTRING for new class RandomColorGenerator * fix RandomColorGenerator doctest * introduce the sample colors param in RandomColorGenerator * Update docstrings to include sample_colors param for RandomColorGenerator * fix cyclic import issues * fix indentations for code blocks * docstring formatting changes * removed performance warning added class method --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Benjamin Hackl <[email protected]>
1 parent 23a2df1 commit c9707f2

File tree

1 file changed

+113
-5
lines changed

1 file changed

+113
-5
lines changed

manim/utils/color/core.py

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1501,17 +1501,124 @@ def random_bright_color() -> ManimColor:
15011501
def random_color() -> ManimColor:
15021502
"""Return a random :class:`ManimColor`.
15031503
1504-
.. warning::
1505-
This operation is very expensive. Please keep in mind the performance loss.
1506-
15071504
Returns
15081505
-------
15091506
ManimColor
15101507
A random :class:`ManimColor`.
15111508
"""
1512-
import manim.utils.color.manim_colors as manim_colors
1509+
return RandomColorGenerator._random_color()
1510+
1511+
1512+
class RandomColorGenerator:
1513+
_singleton: RandomColorGenerator | None = None
1514+
"""A generator for producing random colors from a given list of Manim colors,
1515+
optionally in a reproducible sequence using a seed value.
1516+
1517+
When initialized with a specific seed, this class produces a deterministic
1518+
sequence of :class:`.ManimColor` instances. If no seed is provided, the selection is
1519+
non-deterministic using Python’s global random state.
1520+
1521+
Parameters
1522+
----------
1523+
seed
1524+
A seed value to initialize the internal random number generator.
1525+
If ``None`` (the default), colors are chosen using the global random state.
1526+
1527+
sample_colors
1528+
A custom list of Manim colors to sample from. Defaults to the full Manim
1529+
color palette.
1530+
1531+
Examples
1532+
--------
1533+
Without a seed (non-deterministic)::
1534+
1535+
>>> from manim import RandomColorGenerator, ManimColor, RED, GREEN, BLUE
1536+
>>> rnd = RandomColorGenerator()
1537+
>>> isinstance(rnd.next(), ManimColor)
1538+
True
1539+
1540+
With a seed (deterministic sequence)::
1541+
1542+
>>> rnd = RandomColorGenerator(42)
1543+
>>> rnd.next()
1544+
ManimColor('#ECE7E2')
1545+
>>> rnd.next()
1546+
ManimColor('#BBBBBB')
1547+
>>> rnd.next()
1548+
ManimColor('#BBBBBB')
1549+
1550+
Re-initializing with the same seed gives the same sequence::
1551+
1552+
>>> rnd2 = RandomColorGenerator(42)
1553+
>>> rnd2.next()
1554+
ManimColor('#ECE7E2')
1555+
>>> rnd2.next()
1556+
ManimColor('#BBBBBB')
1557+
>>> rnd2.next()
1558+
ManimColor('#BBBBBB')
1559+
1560+
Using a custom color list::
1561+
1562+
>>> custom_palette = [RED, GREEN, BLUE]
1563+
>>> rnd_custom = RandomColorGenerator(1, sample_colors=custom_palette)
1564+
>>> rnd_custom.next() in custom_palette
1565+
True
1566+
>>> rnd_custom.next() in custom_palette
1567+
True
1568+
1569+
Without a seed and custom palette (non-deterministic)::
1570+
1571+
>>> rnd_nodet = RandomColorGenerator(sample_colors=[RED])
1572+
>>> rnd_nodet.next()
1573+
ManimColor('#FC6255')
1574+
"""
1575+
1576+
def __init__(
1577+
self,
1578+
seed: int | None = None,
1579+
sample_colors: list[ManimColor] | None = None,
1580+
) -> None:
1581+
self.choice = random.choice if seed is None else random.Random(seed).choice
1582+
1583+
from manim.utils.color.manim_colors import _all_manim_colors
15131584

1514-
return random.choice(manim_colors._all_manim_colors)
1585+
self.colors = _all_manim_colors if sample_colors is None else sample_colors
1586+
1587+
def next(self) -> ManimColor:
1588+
"""Returns the next color from the configured color list.
1589+
1590+
Returns
1591+
-------
1592+
ManimColor
1593+
A randomly selected color from the specified color list.
1594+
1595+
Examples
1596+
--------
1597+
Usage::
1598+
1599+
>>> from manim import RandomColorGenerator, RED
1600+
>>> rnd = RandomColorGenerator(sample_colors=[RED])
1601+
>>> rnd.next()
1602+
ManimColor('#FC6255')
1603+
"""
1604+
return self.choice(self.colors)
1605+
1606+
@classmethod
1607+
def _random_color(cls) -> ManimColor:
1608+
"""Internal method to generate a random color using the singleton instance of
1609+
`RandomColorGenerator`.
1610+
It will be used by proxy method `random_color` publicly available
1611+
and makes it backwards compatible.
1612+
1613+
Returns
1614+
-------
1615+
ManimColor:
1616+
A randomly selected color from the configured color list of
1617+
the singleton instance.
1618+
"""
1619+
if cls._singleton is None:
1620+
cls._singleton = cls()
1621+
return cls._singleton.next()
15151622

15161623

15171624
def get_shaded_rgb(
@@ -1567,6 +1674,7 @@ def get_shaded_rgb(
15671674
"average_color",
15681675
"random_bright_color",
15691676
"random_color",
1677+
"RandomColorGenerator",
15701678
"get_shaded_rgb",
15711679
"HSV",
15721680
"RGBA",

0 commit comments

Comments
 (0)