From a207b4a55cc9d3cc50b31ff353370552486a88d8 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Thu, 7 Aug 2025 06:25:31 +0200 Subject: [PATCH 1/3] Made a copy of `color_gradient` that is guaranteed to return a list. --- manim/mobject/graphing/coordinate_systems.py | 4 +- manim/mobject/graphing/probability.py | 6 +-- manim/mobject/mobject.py | 4 +- manim/mobject/opengl/opengl_mobject.py | 4 +- .../opengl/opengl_point_cloud_mobject.py | 4 +- manim/mobject/text/text_mobject.py | 9 ++--- manim/mobject/types/point_cloud_mobject.py | 4 +- manim/utils/color/core.py | 37 +++++++++++++++++++ 8 files changed, 52 insertions(+), 20 deletions(-) diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index e19bc99bc1..4cd0b3cafc 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -47,7 +47,7 @@ YELLOW, ManimColor, ParsableManimColor, - color_gradient, + color_gradient_as_list, interpolate_color, invert_color, ) @@ -1293,7 +1293,7 @@ def construct(self): else: color = [ManimColor(color)] - colors = color_gradient(color, len(x_range_array)) + colors = color_gradient_as_list(color, len(x_range_array)) for x, color in zip(x_range_array, colors): if input_sample_type == "left": diff --git a/manim/mobject/graphing/probability.py b/manim/mobject/graphing/probability.py index 309e0b7ec2..e8be76a3c5 100644 --- a/manim/mobject/graphing/probability.py +++ b/manim/mobject/graphing/probability.py @@ -27,7 +27,7 @@ MAROON_B, YELLOW, ParsableManimColor, - color_gradient, + color_gradient_as_list, ) from manim.utils.iterables import tuplify @@ -103,9 +103,7 @@ def get_division_along_dimension( vect: Vector3D, ) -> VGroup: p_list_complete = self.complete_p_list(p_list) - colors_in_gradient = color_gradient(colors, len(p_list_complete)) - - assert isinstance(colors_in_gradient, list) + colors_in_gradient = color_gradient_as_list(colors, len(p_list_complete)) last_point = self.get_edge_center(-vect) parts = VGroup() diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index fc818fd603..37d6826589 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -32,7 +32,7 @@ YELLOW_C, ManimColor, ParsableManimColor, - color_gradient, + color_gradient_as_list, interpolate_color, ) from ..utils.exceptions import MultiAnimationOverrideException @@ -1970,7 +1970,7 @@ def set_submobject_colors_by_gradient(self, *colors: Iterable[ParsableManimColor return self.set_color(*colors) mobs = self.family_members_with_points() - new_colors = color_gradient(colors, len(mobs)) + new_colors = color_gradient_as_list(colors, len(mobs)) for mob, color in zip(mobs, new_colors): mob.set_color(color, family=False) diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index d0a3306f7f..79d07ac300 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -23,7 +23,7 @@ WHITE, ManimColor, ParsableManimColor, - color_gradient, + color_gradient_as_list, color_to_rgb, rgb_to_hex, ) @@ -2176,7 +2176,7 @@ def set_submobject_colors_by_gradient(self, *colors: ParsableManimColor) -> Self # mobs = self.family_members_with_points() mobs = self.submobjects - new_colors = color_gradient(colors, len(mobs)) + new_colors = color_gradient_as_list(colors, len(mobs)) for mob, color in zip(mobs, new_colors): mob.set_color(color) diff --git a/manim/mobject/opengl/opengl_point_cloud_mobject.py b/manim/mobject/opengl/opengl_point_cloud_mobject.py index 72e196fb9b..6141a7f0e6 100644 --- a/manim/mobject/opengl/opengl_point_cloud_mobject.py +++ b/manim/mobject/opengl/opengl_point_cloud_mobject.py @@ -14,7 +14,7 @@ WHITE, YELLOW, ParsableManimColor, - color_gradient, + color_gradient_as_list, color_to_rgba, ) from manim.utils.config_ops import _Uniforms @@ -92,7 +92,7 @@ def thin_func(num_points=num_points): def set_color_by_gradient(self, *colors): self.rgbas = np.array( - list(map(color_to_rgba, color_gradient(*colors, self.get_num_points()))), + map(color_to_rgba, color_gradient_as_list(*colors, self.get_num_points())), ) return self diff --git a/manim/mobject/text/text_mobject.py b/manim/mobject/text/text_mobject.py index c35f874ed5..0e7f53ad55 100644 --- a/manim/mobject/text/text_mobject.py +++ b/manim/mobject/text/text_mobject.py @@ -71,7 +71,7 @@ def construct(self): from manim.mobject.geometry.arc import Dot from manim.mobject.svg.svg_mobject import SVGMobject from manim.mobject.types.vectorized_mobject import VGroup, VMobject -from manim.utils.color import ManimColor, ParsableManimColor, color_gradient +from manim.utils.color import ManimColor, ParsableManimColor, color_gradient_as_list from manim.utils.deprecation import deprecated TEXT_MOB_SCALE_FACTOR = 0.05 @@ -735,17 +735,14 @@ def _get_settings_from_gradient( settings = [] args = copy.copy(default_args) if self.gradient: - colors = color_gradient(self.gradient, len(self.text)) + colors = color_gradient_as_list(self.gradient, len(self.text)) for i in range(len(self.text)): args["color"] = colors[i].to_hex() settings.append(TextSetting(i, i + 1, **args)) for word, gradient in self.t2g.items(): - if isinstance(gradient, str) or len(gradient) == 1: - color = gradient if isinstance(gradient, str) else gradient[0] - gradient = [ManimColor(color)] colors = ( - color_gradient(gradient, len(word)) + color_gradient_as_list(gradient, len(word)) if len(gradient) != 1 else len(word) * gradient ) diff --git a/manim/mobject/types/point_cloud_mobject.py b/manim/mobject/types/point_cloud_mobject.py index e88980dd34..10118a7b3b 100644 --- a/manim/mobject/types/point_cloud_mobject.py +++ b/manim/mobject/types/point_cloud_mobject.py @@ -20,7 +20,7 @@ YELLOW, ManimColor, ParsableManimColor, - color_gradient, + color_gradient_as_list, color_to_rgba, rgba_to_color, ) @@ -124,7 +124,7 @@ def set_stroke_width(self, width: int, family: bool = True) -> Self: def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self: self.rgbas = np.array( - list(map(color_to_rgba, color_gradient(*colors, len(self.points)))), + map(color_to_rgba, color_gradient_as_list(*colors, len(self.points))), ) return self diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index af25992e59..356418302b 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1442,6 +1442,42 @@ def color_gradient( ] +def color_gradient_as_list( + reference_colors: Sequence[ParsableManimColor], + length_of_output: int, +) -> list[ManimColor]: + """Create a list of colors interpolated between the input array of colors with a + specific number of colors. + + Parameters + ---------- + reference_colors + The colors to be interpolated between or spread apart. + length_of_output + The number of colors that the output should have, ideally more than the input. + + Returns + ------- + list[ManimColor] + A list of interpolated :class:`ManimColor`'s. + """ + if length_of_output == 0: + return [ManimColor(reference_colors[0])] + if len(reference_colors) == 1: + return [ManimColor(reference_colors[0])] * length_of_output + rgbs = [color_to_rgb(color) for color in reference_colors] + alphas = np.linspace(0, (len(rgbs) - 1), length_of_output) + floors = alphas.astype("int") + alphas_mod1 = alphas % 1 + # End edge case + alphas_mod1[-1] = 1 + floors[-1] = len(rgbs) - 2 + return [ + rgb_to_color((rgbs[i] * (1 - alpha)) + (rgbs[i + 1] * alpha)) + for i, alpha in zip(floors, alphas_mod1) + ] + + def interpolate_color( color1: ManimColorT, color2: ManimColorT, alpha: float ) -> ManimColorT: @@ -1670,6 +1706,7 @@ def get_shaded_rgb( "hex_to_rgb", "invert_color", "color_gradient", + "color_gradient_as_list", "interpolate_color", "average_color", "random_bright_color", From 4295fc17f540ed069ecc8cdee058bb79c11a6ee1 Mon Sep 17 00:00:00 2001 From: Henrik Skov Midtiby Date: Mon, 11 Aug 2025 20:45:06 +0200 Subject: [PATCH 2/3] Implement suggestions by Chopan50 --- manim/mobject/graphing/coordinate_systems.py | 4 +- manim/mobject/graphing/probability.py | 4 +- manim/mobject/mobject.py | 4 +- manim/mobject/opengl/opengl_mobject.py | 4 +- .../opengl/opengl_point_cloud_mobject.py | 4 +- manim/mobject/text/text_mobject.py | 6 +- manim/mobject/types/point_cloud_mobject.py | 4 +- manim/utils/color/core.py | 56 ++++--------------- 8 files changed, 27 insertions(+), 59 deletions(-) diff --git a/manim/mobject/graphing/coordinate_systems.py b/manim/mobject/graphing/coordinate_systems.py index 4cd0b3cafc..e19bc99bc1 100644 --- a/manim/mobject/graphing/coordinate_systems.py +++ b/manim/mobject/graphing/coordinate_systems.py @@ -47,7 +47,7 @@ YELLOW, ManimColor, ParsableManimColor, - color_gradient_as_list, + color_gradient, interpolate_color, invert_color, ) @@ -1293,7 +1293,7 @@ def construct(self): else: color = [ManimColor(color)] - colors = color_gradient_as_list(color, len(x_range_array)) + colors = color_gradient(color, len(x_range_array)) for x, color in zip(x_range_array, colors): if input_sample_type == "left": diff --git a/manim/mobject/graphing/probability.py b/manim/mobject/graphing/probability.py index e8be76a3c5..d316379133 100644 --- a/manim/mobject/graphing/probability.py +++ b/manim/mobject/graphing/probability.py @@ -27,7 +27,7 @@ MAROON_B, YELLOW, ParsableManimColor, - color_gradient_as_list, + color_gradient, ) from manim.utils.iterables import tuplify @@ -103,7 +103,7 @@ def get_division_along_dimension( vect: Vector3D, ) -> VGroup: p_list_complete = self.complete_p_list(p_list) - colors_in_gradient = color_gradient_as_list(colors, len(p_list_complete)) + colors_in_gradient = color_gradient(colors, len(p_list_complete)) last_point = self.get_edge_center(-vect) parts = VGroup() diff --git a/manim/mobject/mobject.py b/manim/mobject/mobject.py index 37d6826589..fc818fd603 100644 --- a/manim/mobject/mobject.py +++ b/manim/mobject/mobject.py @@ -32,7 +32,7 @@ YELLOW_C, ManimColor, ParsableManimColor, - color_gradient_as_list, + color_gradient, interpolate_color, ) from ..utils.exceptions import MultiAnimationOverrideException @@ -1970,7 +1970,7 @@ def set_submobject_colors_by_gradient(self, *colors: Iterable[ParsableManimColor return self.set_color(*colors) mobs = self.family_members_with_points() - new_colors = color_gradient_as_list(colors, len(mobs)) + new_colors = color_gradient(colors, len(mobs)) for mob, color in zip(mobs, new_colors): mob.set_color(color, family=False) diff --git a/manim/mobject/opengl/opengl_mobject.py b/manim/mobject/opengl/opengl_mobject.py index 79d07ac300..d0a3306f7f 100644 --- a/manim/mobject/opengl/opengl_mobject.py +++ b/manim/mobject/opengl/opengl_mobject.py @@ -23,7 +23,7 @@ WHITE, ManimColor, ParsableManimColor, - color_gradient_as_list, + color_gradient, color_to_rgb, rgb_to_hex, ) @@ -2176,7 +2176,7 @@ def set_submobject_colors_by_gradient(self, *colors: ParsableManimColor) -> Self # mobs = self.family_members_with_points() mobs = self.submobjects - new_colors = color_gradient_as_list(colors, len(mobs)) + new_colors = color_gradient(colors, len(mobs)) for mob, color in zip(mobs, new_colors): mob.set_color(color) diff --git a/manim/mobject/opengl/opengl_point_cloud_mobject.py b/manim/mobject/opengl/opengl_point_cloud_mobject.py index 6141a7f0e6..72e196fb9b 100644 --- a/manim/mobject/opengl/opengl_point_cloud_mobject.py +++ b/manim/mobject/opengl/opengl_point_cloud_mobject.py @@ -14,7 +14,7 @@ WHITE, YELLOW, ParsableManimColor, - color_gradient_as_list, + color_gradient, color_to_rgba, ) from manim.utils.config_ops import _Uniforms @@ -92,7 +92,7 @@ def thin_func(num_points=num_points): def set_color_by_gradient(self, *colors): self.rgbas = np.array( - map(color_to_rgba, color_gradient_as_list(*colors, self.get_num_points())), + list(map(color_to_rgba, color_gradient(*colors, self.get_num_points()))), ) return self diff --git a/manim/mobject/text/text_mobject.py b/manim/mobject/text/text_mobject.py index 0e7f53ad55..42b57b4ae1 100644 --- a/manim/mobject/text/text_mobject.py +++ b/manim/mobject/text/text_mobject.py @@ -71,7 +71,7 @@ def construct(self): from manim.mobject.geometry.arc import Dot from manim.mobject.svg.svg_mobject import SVGMobject from manim.mobject.types.vectorized_mobject import VGroup, VMobject -from manim.utils.color import ManimColor, ParsableManimColor, color_gradient_as_list +from manim.utils.color import ManimColor, ParsableManimColor, color_gradient from manim.utils.deprecation import deprecated TEXT_MOB_SCALE_FACTOR = 0.05 @@ -735,14 +735,14 @@ def _get_settings_from_gradient( settings = [] args = copy.copy(default_args) if self.gradient: - colors = color_gradient_as_list(self.gradient, len(self.text)) + colors = color_gradient(self.gradient, len(self.text)) for i in range(len(self.text)): args["color"] = colors[i].to_hex() settings.append(TextSetting(i, i + 1, **args)) for word, gradient in self.t2g.items(): colors = ( - color_gradient_as_list(gradient, len(word)) + color_gradient(gradient, len(word)) if len(gradient) != 1 else len(word) * gradient ) diff --git a/manim/mobject/types/point_cloud_mobject.py b/manim/mobject/types/point_cloud_mobject.py index 10118a7b3b..e88980dd34 100644 --- a/manim/mobject/types/point_cloud_mobject.py +++ b/manim/mobject/types/point_cloud_mobject.py @@ -20,7 +20,7 @@ YELLOW, ManimColor, ParsableManimColor, - color_gradient_as_list, + color_gradient, color_to_rgba, rgba_to_color, ) @@ -124,7 +124,7 @@ def set_stroke_width(self, width: int, family: bool = True) -> Self: def set_color_by_gradient(self, *colors: ParsableManimColor) -> Self: self.rgbas = np.array( - map(color_to_rgba, color_gradient_as_list(*colors, len(self.points))), + list(map(color_to_rgba, color_gradient(*colors, len(self.points)))), ) return self diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 356418302b..211a0a8b86 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -69,7 +69,7 @@ # logger = _config.logger import random import re -from collections.abc import Sequence +from collections.abc import Iterable, Sequence from typing import TypeVar, Union, overload import numpy as np @@ -1407,43 +1407,7 @@ def invert_color(color: ManimColorT) -> ManimColorT: def color_gradient( - reference_colors: Sequence[ParsableManimColor], - length_of_output: int, -) -> list[ManimColor] | ManimColor: - """Create a list of colors interpolated between the input array of colors with a - specific number of colors. - - Parameters - ---------- - reference_colors - The colors to be interpolated between or spread apart. - length_of_output - The number of colors that the output should have, ideally more than the input. - - Returns - ------- - list[ManimColor] | ManimColor - A :class:`ManimColor` or a list of interpolated :class:`ManimColor`'s. - """ - if length_of_output == 0: - return ManimColor(reference_colors[0]) - if len(reference_colors) == 1: - return [ManimColor(reference_colors[0])] * length_of_output - rgbs = [color_to_rgb(color) for color in reference_colors] - alphas = np.linspace(0, (len(rgbs) - 1), length_of_output) - floors = alphas.astype("int") - alphas_mod1 = alphas % 1 - # End edge case - alphas_mod1[-1] = 1 - floors[-1] = len(rgbs) - 2 - return [ - rgb_to_color((rgbs[i] * (1 - alpha)) + (rgbs[i + 1] * alpha)) - for i, alpha in zip(floors, alphas_mod1) - ] - - -def color_gradient_as_list( - reference_colors: Sequence[ParsableManimColor], + reference_colors: Iterable[ParsableManimColor], length_of_output: int, ) -> list[ManimColor]: """Create a list of colors interpolated between the input array of colors with a @@ -1462,11 +1426,16 @@ def color_gradient_as_list( A list of interpolated :class:`ManimColor`'s. """ if length_of_output == 0: - return [ManimColor(reference_colors[0])] - if len(reference_colors) == 1: - return [ManimColor(reference_colors[0])] * length_of_output - rgbs = [color_to_rgb(color) for color in reference_colors] - alphas = np.linspace(0, (len(rgbs) - 1), length_of_output) + return [] + parsed_colors = [ManimColor(color) for color in reference_colors] + num_colors = len(parsed_colors) + if num_colors == 0: + raise ValueError("Expected 1 or more reference colors. Got 0 colors.") + if num_colors == 1: + return parsed_colors * length_of_output + + rgbs = [color.to_rgb() for color in parsed_colors] + alphas = np.linspace(0, (num_colors - 1), length_of_output) floors = alphas.astype("int") alphas_mod1 = alphas % 1 # End edge case @@ -1706,7 +1675,6 @@ def get_shaded_rgb( "hex_to_rgb", "invert_color", "color_gradient", - "color_gradient_as_list", "interpolate_color", "average_color", "random_bright_color", From c818f4afbfc787cb3c5620bb6d932b8b43f21cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Manr=C3=ADquez=20Novoa?= <49853152+chopan050@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:58:08 -0400 Subject: [PATCH 3/3] Change last len(rgbs) to num_colors in color_gradient --- manim/utils/color/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manim/utils/color/core.py b/manim/utils/color/core.py index 211a0a8b86..62a93a1426 100644 --- a/manim/utils/color/core.py +++ b/manim/utils/color/core.py @@ -1440,7 +1440,7 @@ def color_gradient( alphas_mod1 = alphas % 1 # End edge case alphas_mod1[-1] = 1 - floors[-1] = len(rgbs) - 2 + floors[-1] = num_colors - 2 return [ rgb_to_color((rgbs[i] * (1 - alpha)) + (rgbs[i + 1] * alpha)) for i, alpha in zip(floors, alphas_mod1)