diff --git a/manim/camera/camera.py b/manim/camera/camera.py index 1d54e5cf95..9d77de445b 100644 --- a/manim/camera/camera.py +++ b/manim/camera/camera.py @@ -18,6 +18,7 @@ from manim._config import config, logger from manim.constants import * +from manim.constants import GradientType from manim.mobject.mobject import Mobject from manim.mobject.types.point_cloud_mobject import PMobject from manim.mobject.types.vectorized_mobject import VMobject @@ -790,9 +791,28 @@ def set_cairo_context_color( # encodes it in reverse order ctx.set_source_rgba(*rgbas[0][2::-1], rgbas[0][3]) else: - points = vmobject.get_gradient_start_and_end_points() - points = self.transform_points_pre_display(vmobject, points) - pat = cairo.LinearGradient(*it.chain(*(point[:2] for point in points))) + match vmobject.gradient_type: + case GradientType.LINEAR: + points = vmobject.get_gradient_start_and_end_points() + points = self.transform_points_pre_display(vmobject, points) + pat = cairo.LinearGradient( + *it.chain(*(point[:2] for point in points)) + ) + case GradientType.RADIAL: + radius = np.hypot(vmobject.get_width(), vmobject.get_height()) / 2 + vmobject_center = vmobject.get_center() + pat = cairo.RadialGradient( + vmobject_center[0], + vmobject_center[1], + 0, + vmobject_center[0], + vmobject_center[1], + radius, + ) + case _: + raise ValueError( + f"Gradient type {vmobject.gradient_type} does not exist or is not supported" + ) offsets = np.linspace(0, 1, len(rgbas)) for rgba, offset in zip(rgbas, offsets, strict=True): pat.add_color_stop_rgba(offset, *rgba[2::-1], rgba[3]) diff --git a/manim/constants.py b/manim/constants.py index ccf99a0293..a2d781d319 100644 --- a/manim/constants.py +++ b/manim/constants.py @@ -76,6 +76,7 @@ "RendererType", "LineJointType", "CapStyleType", + "GradientType", ] # Messages @@ -347,3 +348,42 @@ def construct(self): ROUND = 1 BUTT = 2 SQUARE = 3 + + +class GradientType(Enum): + """Collection of available gradient types. + + See the example below for a visual illustration of the different + gradient types. + + Examples + -------- + + .. manim:: GradientVariants + :save_last_frame: + + class GradientVariants(Scene): + def construct(self): + gradient = color_gradient([RED, GREEN, BLUE], 3) + circles = VGroup(*[ + Circle( + fill_opacity=1, + fill_color=gradient, + stroke_opacity=0, + gradient_type=gradient_type, + sheen_direction=RIGHT + ).scale(2) + for gradient_type in GradientType + ]) + circles.arrange(RIGHT, buff=1) + self.add(circles) + for circle in circles: + label = Text( + circle.gradient_type.name, + font_size=24 + ).next_to(circle, DOWN) + self.add(label) + """ + + LINEAR = 0 + RADIAL = 1 diff --git a/manim/mobject/types/vectorized_mobject.py b/manim/mobject/types/vectorized_mobject.py index 791b28bf5a..922c5c8a4a 100644 --- a/manim/mobject/types/vectorized_mobject.py +++ b/manim/mobject/types/vectorized_mobject.py @@ -101,6 +101,10 @@ class VMobject(Mobject): The line joint type used to connect the curve segments of this vectorized mobject. See :class:`.LineJointType` for options. + gradient_type + Indicates the type of gradient to be applied. Options + are GradientType.LINEAR and GradientType.RADIAL. + See :class:`.GradientType` """ sheen_factor = 0.0 @@ -128,6 +132,7 @@ def __init__( tolerance_for_point_equality: float = 1e-6, n_points_per_cubic_curve: int = 4, cap_style: CapStyleType = CapStyleType.AUTO, + gradient_type: GradientType = GradientType.LINEAR, **kwargs: Any, ): self.fill_opacity = fill_opacity @@ -159,6 +164,7 @@ def __init__( 0, 1, n_points_per_cubic_curve ) self.cap_style: CapStyleType = cap_style + self.gradient_type = gradient_type super().__init__(**kwargs) self.submobjects: list[VMobject]