From bec30bbaa8b681d3a8cbd4030fe1667c81547eb2 Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Tue, 3 Jun 2025 18:39:02 +0530 Subject: [PATCH 01/10] add a random generator to hold the seed for random generation --- manim/utils/color/core.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index d8731f5d1b..a39f6652c2 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1508,10 +1508,17 @@ def random_color() -> ManimColor: ManimColor A random :class:`ManimColor`. """ - import manim.utils.color.manim_colors as manim_colors - - return random.choice(manim_colors._all_manim_colors) - + return RandomColorGenerator().next() + +class RandomColorGenerator: + """A class to generate random colors from the Manim color palette in a reproducible manner using a seed value.""" + def __init__(self, seed: int | None = None) -> None: + self.choice = random.choice if seed is None else random.Random(seed).choice + + def next(self) -> ManimColor: + import manim.utils.color.manim_colors as manim_colors + + return self.choice(manim_colors._all_manim_colors) def get_shaded_rgb( rgb: RGB_Array_Float, From 09e32f72af97fa58078aded9f7b70fc774b5a984 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:42:53 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- manim/utils/color/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index a39f6652c2..ec76461545 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1510,16 +1510,19 @@ def random_color() -> ManimColor: """ return RandomColorGenerator().next() + class RandomColorGenerator: """A class to generate random colors from the Manim color palette in a reproducible manner using a seed value.""" + def __init__(self, seed: int | None = None) -> None: self.choice = random.choice if seed is None else random.Random(seed).choice - + def next(self) -> ManimColor: import manim.utils.color.manim_colors as manim_colors - + return self.choice(manim_colors._all_manim_colors) + def get_shaded_rgb( rgb: RGB_Array_Float, point: Point3D, From 8e18ed7bfb0ca09ca73df64b6435f4507c31b8f2 Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Thu, 5 Jun 2025 13:07:15 +0530 Subject: [PATCH 03/10] add DOCSTRING for new class RandomColorGenerator --- manim/utils/color/core.py | 59 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index ec76461545..864b2dbf21 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1512,12 +1512,68 @@ def random_color() -> ManimColor: class RandomColorGenerator: - """A class to generate random colors from the Manim color palette in a reproducible manner using a seed value.""" + """ + A generator for producing random colors from the Manim color palette, + optionally in a reproducible sequence using a seed value. + + When initialized with a specific seed, this class generates a deterministic + sequence of Manim colors, which is useful for reproducibility in testing or animations. + If no seed is provided, it behaves like Python’s standard `random.choice`, yielding + different results on each run. + + Parameters + ---------- + seed : int | None, optional + A seed value to initialize the internal random number generator. + If None (default), colors are chosen using the global random state. + + Examples + -------- + Without a seed (non-deterministic): + >>> from manim.utils.color.core import RandomColorGenerator + >>> rnd = RandomColorGenerator() + >>> isinstance(rnd.next().hex, str) + 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 produces the same sequence: + >>> rnd2 = RandomColorGenerator(42) + >>> rnd2.next() + ManimColor('#ECE7E2') + >>> rnd2.next() + ManimColor('#BBBBBB') + >>> rnd2.next() + ManimColor('#BBBBBB') + """ def __init__(self, seed: int | None = None) -> None: self.choice = random.choice if seed is None else random.Random(seed).choice def next(self) -> ManimColor: + """ + Returns the next color from the Manim color palette. + + Returns + ------- + ManimColor + A color randomly selected from the predefined Manim color palette. + + Examples + -------- + >>> rnd = RandomColorGenerator(23) + >>> rnd.next() + ManimColor('#A6CF8C') + >>> rnd.next() + ManimColor('#222222') + """ import manim.utils.color.manim_colors as manim_colors return self.choice(manim_colors._all_manim_colors) @@ -1576,6 +1632,7 @@ def get_shaded_rgb( "average_color", "random_bright_color", "random_color", + "RandomColorGenerator", "get_shaded_rgb", "HSV", "RGBA", From 15d49dca0d9651615e26c9b226d2b8efff957ac9 Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Thu, 5 Jun 2025 13:17:08 +0530 Subject: [PATCH 04/10] fix RandomColorGenerator doctest --- manim/utils/color/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 864b2dbf21..5c7da4505c 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1530,11 +1530,12 @@ class RandomColorGenerator: Examples -------- Without a seed (non-deterministic): - >>> from manim.utils.color.core import RandomColorGenerator + >>> from manim import RandomColorGenerator, ManimColor >>> rnd = RandomColorGenerator() - >>> isinstance(rnd.next().hex, str) + >>> isinstance(rnd.next(), ManimColor) True + With a seed (deterministic sequence): >>> rnd = RandomColorGenerator(42) >>> rnd.next() From c92a1c71e9b31b7b927f5c47ae74198e18023a0d Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Thu, 5 Jun 2025 13:32:05 +0530 Subject: [PATCH 05/10] introduce the sample colors param in RandomColorGenerator --- manim/utils/color/core.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 5c7da4505c..398b50d267 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1555,8 +1555,15 @@ class RandomColorGenerator: ManimColor('#BBBBBB') """ - def __init__(self, seed: int | None = None) -> None: + import manim.utils.color.manim_colors as manim_colors + + def __init__( + self, + seed: int | None = None, + sample_colors: list[ManimColor] = manim_colors._all_manim_colors, + ) -> None: self.choice = random.choice if seed is None else random.Random(seed).choice + self.colors = sample_colors def next(self) -> ManimColor: """ @@ -1575,9 +1582,7 @@ def next(self) -> ManimColor: >>> rnd.next() ManimColor('#222222') """ - import manim.utils.color.manim_colors as manim_colors - - return self.choice(manim_colors._all_manim_colors) + return self.choice(self.colors) def get_shaded_rgb( From 96a11966cc822350931bbb71023c15a5ca6014cc Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Thu, 5 Jun 2025 13:36:37 +0530 Subject: [PATCH 06/10] Update docstrings to include sample_colors param for RandomColorGenerator --- manim/utils/color/core.py | 41 ++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 398b50d267..5ba35fc063 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1513,13 +1513,12 @@ def random_color() -> ManimColor: class RandomColorGenerator: """ - A generator for producing random colors from the Manim color palette, + 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 generates a deterministic - sequence of Manim colors, which is useful for reproducibility in testing or animations. - If no seed is provided, it behaves like Python’s standard `random.choice`, yielding - different results on each run. + When initialized with a specific seed, this class produces a deterministic + sequence of `ManimColor` instances. If no seed is provided, the selection is + non-deterministic using Python’s global random state. Parameters ---------- @@ -1527,15 +1526,17 @@ class RandomColorGenerator: A seed value to initialize the internal random number generator. If None (default), colors are chosen using the global random state. + sample_colors : list[ManimColor], optional + 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 + >>> 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() @@ -1545,7 +1546,7 @@ class RandomColorGenerator: >>> rnd.next() ManimColor('#BBBBBB') - Re-initializing with the same seed produces the same sequence: + Re-initializing with the same seed gives the same sequence: >>> rnd2 = RandomColorGenerator(42) >>> rnd2.next() ManimColor('#ECE7E2') @@ -1553,6 +1554,19 @@ class RandomColorGenerator: 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') """ import manim.utils.color.manim_colors as manim_colors @@ -1567,20 +1581,19 @@ def __init__( def next(self) -> ManimColor: """ - Returns the next color from the Manim color palette. + Returns the next color from the configured color list. Returns ------- ManimColor - A color randomly selected from the predefined Manim color palette. + A randomly selected color from the specified color list. Examples -------- - >>> rnd = RandomColorGenerator(23) - >>> rnd.next() - ManimColor('#A6CF8C') + >>> from manim import RandomColorGenerator, RED + >>> rnd = RandomColorGenerator(sample_colors=[RED]) >>> rnd.next() - ManimColor('#222222') + ManimColor('#FC6255') """ return self.choice(self.colors) From ec0e8711007807010325334de291fa51d3f1a92d Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Thu, 5 Jun 2025 14:00:36 +0530 Subject: [PATCH 07/10] fix cyclic import issues --- manim/utils/color/core.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 5ba35fc063..2f748e6508 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1569,15 +1569,16 @@ class RandomColorGenerator: ManimColor('#FC6255') """ - import manim.utils.color.manim_colors as manim_colors - def __init__( self, seed: int | None = None, - sample_colors: list[ManimColor] = manim_colors._all_manim_colors, + sample_colors: list[ManimColor] | None = None, ) -> None: self.choice = random.choice if seed is None else random.Random(seed).choice - self.colors = sample_colors + + from manim.utils.color.manim_colors import _all_manim_colors + + self.colors = _all_manim_colors if sample_colors is None else sample_colors def next(self) -> ManimColor: """ From fc2c24d64e1ae2bafc9ac412a5914de9ffbb868b Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Sat, 7 Jun 2025 12:22:16 +0200 Subject: [PATCH 08/10] fix indentations for code blocks --- manim/utils/color/core.py | 77 +++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 2f748e6508..b8feee6842 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1531,42 +1531,47 @@ class RandomColorGenerator: 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') + 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__( From d1af7757cd039b773c44aaf8b3ef0f39fb87d9a7 Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Sun, 8 Jun 2025 14:30:20 +0530 Subject: [PATCH 09/10] docstring formatting changes --- manim/utils/color/core.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index b8feee6842..742bd61179 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1512,22 +1512,22 @@ def random_color() -> ManimColor: class RandomColorGenerator: - """ - 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 + 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 + seed A seed value to initialize the internal random number generator. - 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 - A custom list of Manim colors to sample from. Defaults to the full Manim color palette. + sample_colors + A custom list of Manim colors to sample from. Defaults to the full Manim + color palette. Examples -------- @@ -1586,8 +1586,7 @@ def __init__( 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. + """Returns the next color from the configured color list. Returns ------- @@ -1596,10 +1595,12 @@ def next(self) -> ManimColor: Examples -------- - >>> from manim import RandomColorGenerator, RED - >>> rnd = RandomColorGenerator(sample_colors=[RED]) - >>> rnd.next() - ManimColor('#FC6255') + Usage:: + + >>> from manim import RandomColorGenerator, RED + >>> rnd = RandomColorGenerator(sample_colors=[RED]) + >>> rnd.next() + ManimColor('#FC6255') """ return self.choice(self.colors) From 95c1a9f7ffc07dc8bd4cdd36274b1a890f04785c Mon Sep 17 00:00:00 2001 From: Ishu Bansal Date: Sun, 8 Jun 2025 18:21:03 +0530 Subject: [PATCH 10/10] removed performance warning added class method --- manim/utils/color/core.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 742bd61179..b2c4f3229c 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1500,18 +1500,16 @@ def random_bright_color() -> ManimColor: def random_color() -> ManimColor: """Return a random :class:`ManimColor`. - .. warning:: - This operation is very expensive. Please keep in mind the performance loss. - Returns ------- ManimColor A random :class:`ManimColor`. """ - return RandomColorGenerator().next() + return RandomColorGenerator._random_color() class RandomColorGenerator: + _singleton: RandomColorGenerator | None = None """A generator for producing random colors from a given list of Manim colors, optionally in a reproducible sequence using a seed value. @@ -1604,6 +1602,23 @@ def next(self) -> ManimColor: """ return self.choice(self.colors) + @classmethod + def _random_color(cls) -> ManimColor: + """Internal method to generate a random color using the singleton instance of + `RandomColorGenerator`. + It will be used by proxy method `random_color` publicly available + and makes it backwards compatible. + + Returns + ------- + ManimColor: + A randomly selected color from the configured color list of + the singleton instance. + """ + if cls._singleton is None: + cls._singleton = cls() + return cls._singleton.next() + def get_shaded_rgb( rgb: RGB_Array_Float,