Skip to content
6 changes: 3 additions & 3 deletions manim/animation/rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

__all__ = ["Rotating", "Rotate"]

from collections.abc import Sequence
from typing import TYPE_CHECKING, Callable

import numpy as np
Expand All @@ -16,6 +15,7 @@

if TYPE_CHECKING:
from ..mobject.mobject import Mobject
from ..typing import Vector3D


class Rotating(Animation):
Expand Down Expand Up @@ -87,8 +87,8 @@ def __init__(
mobject: Mobject,
angle: float = PI,
axis: np.ndarray = OUT,
about_point: Sequence[float] | None = None,
about_edge: Sequence[float] | None = None,
about_point: Vector3D | None = None,
about_edge: Vector3D | None = None,
**kwargs,
) -> None:
if "path_arc" not in kwargs:
Expand Down
3 changes: 2 additions & 1 deletion manim/mobject/geometry/arc.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,10 +679,11 @@ def __init__(
point: Point3D = ORIGIN,
radius: float = DEFAULT_DOT_RADIUS,
stroke_width: float = 0,
fill_opacity: float = 1.0,
fill_opacity: float = None,
Comment thread
chopan050 marked this conversation as resolved.
Outdated
color: ParsableManimColor = WHITE,
**kwargs,
) -> None:
self.submobjects = []
super().__init__(
arc_center=point,
radius=radius,
Expand Down
8 changes: 5 additions & 3 deletions manim/mobject/mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def __init_subclass__(cls, **kwargs) -> None:

def __init__(
self,
color: ParsableManimColor | list[ParsableManimColor] = WHITE,
color: ParsableManimColor | list[ParsableManimColor] | None = WHITE,
name: str | None = None,
dim: int = 3,
target=None,
Expand Down Expand Up @@ -400,13 +400,15 @@ def __deepcopy__(self, clone_from_id) -> Self:
return result

def __repr__(self) -> str:
return str(self.name)
if hasattr(self, "name"):
return str(self.name)
return str(self.__class__.__name__)

def reset_points(self) -> None:
"""Sets :attr:`points` to be an empty array."""
self.points = np.zeros((0, self.dim))

def init_colors(self) -> None:
def init_colors(self) -> Self:
"""Initializes the colors.

Gets called upon creation. This is an empty method that can be implemented by
Expand Down
98 changes: 50 additions & 48 deletions manim/mobject/types/vectorized_mobject.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from __future__ import annotations

from typing_extensions import deprecated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Maybe use manim.utils.deprecation.deprecated?


__all__ = [
"VMobject",
"VGroup",
Expand All @@ -15,7 +17,7 @@
import itertools as it
import sys
from collections.abc import Generator, Hashable, Iterable, Mapping, Sequence
from typing import TYPE_CHECKING, Callable, Literal
from typing import TYPE_CHECKING, Callable, Literal, cast

import numpy as np
from PIL.Image import Image
Expand Down Expand Up @@ -112,13 +114,14 @@ class VMobject(Mobject):

def __init__(
self,
color: ParsableManimColor | list[ParsableManimColor] | None = None,
fill_color: ParsableManimColor | None = None,
fill_opacity: float = 0.0,
fill_opacity: float | None = None,
stroke_color: ParsableManimColor | None = None,
stroke_opacity: float = 1.0,
stroke_opacity: float | None = None,
stroke_width: float = DEFAULT_STROKE_WIDTH,
background_stroke_color: ParsableManimColor | None = BLACK,
background_stroke_opacity: float = 1.0,
background_stroke_opacity: float | None = None,
background_stroke_width: float = 0,
sheen_factor: float = 0.0,
joint_type: LineJointType | None = None,
Expand All @@ -134,14 +137,7 @@ def __init__(
cap_style: CapStyleType = CapStyleType.AUTO,
**kwargs,
):
self.fill_opacity = fill_opacity
self.stroke_opacity = stroke_opacity
self.stroke_width = stroke_width
if background_stroke_color is not None:
self.background_stroke_color: ManimColor = ManimColor(
background_stroke_color
)
self.background_stroke_opacity: float = background_stroke_opacity
self.background_stroke_width: float = background_stroke_width
self.sheen_factor: float = sheen_factor
self.joint_type: LineJointType = (
Expand All @@ -163,17 +159,33 @@ def __init__(
0, 1, n_points_per_cubic_curve
)
self.cap_style: CapStyleType = cap_style
super().__init__(**kwargs)

# TODO: Refactor color initialization
# This must be after init

self.submobjects: list[VMobject]
if fill_color is not None or stroke_color is not None:
color = None

if background_stroke_color is not None:
self.background_stroke_color: ManimColor = ManimColor(
background_stroke_color
)
if background_stroke_opacity is not None:
self.background_stroke_color = self.background_stroke_color.opacity(
background_stroke_opacity
)

super().__init__(color=color, **kwargs)

# TODO: Find where color overwrites are happening and remove the color doubling
# if "color" in kwargs:
# fill_color = kwargs["color"]
# stroke_color = kwargs["color"]
if fill_color is not None:
self.fill_color = ManimColor.parse(fill_color)
# if fill_opacity is not None:
# self.fill_color = self.fill_color.opacity(fill_opacity)
Comment thread Fixed
if stroke_color is not None:
self.stroke_color = ManimColor.parse(stroke_color)
# if stroke_opacity is not None:
# self.stroke_color = self.stroke_color.opacity(stroke_opacity)
Comment thread Fixed

def _assert_valid_submobjects(self, submobjects: Iterable[VMobject]) -> Self:
return self._assert_valid_submobjects_internal(submobjects, VMobject)
Expand All @@ -191,22 +203,11 @@ def get_mobject_type_class() -> type[VMobject]:
return VMobject

# Colors
@deprecated("Sorry")
def init_colors(self, propagate_colors: bool = True) -> Self:
self.set_fill(
color=self.fill_color,
opacity=self.fill_opacity,
family=propagate_colors,
)
self.set_stroke(
color=self.stroke_color,
width=self.stroke_width,
opacity=self.stroke_opacity,
family=propagate_colors,
)
self.set_background_stroke(
color=self.background_stroke_color,
width=self.background_stroke_width,
opacity=self.background_stroke_opacity,
family=propagate_colors,
)
self.set_sheen(
Expand All @@ -222,7 +223,7 @@ def init_colors(self, propagate_colors: bool = True) -> Self:
return self

def generate_rgbas_array(
self, color: ManimColor | list[ManimColor], opacity: float | Iterable[float]
self, color: ManimColor | list[ManimColor]
) -> RGBA_Array_Float:
"""
First arg can be either a color, or a tuple/list of colors.
Expand All @@ -232,14 +233,10 @@ def generate_rgbas_array(
will automatically be added for the gradient
"""
colors: list[ManimColor] = [
ManimColor(c) if (c is not None) else BLACK for c in tuplify(color)
]
opacities: list[float] = [
o if (o is not None) else 0.0 for o in tuplify(opacity)
ManimColor(c) if (c is not None) else BLACK
for c in cast(tuple[ManimColor], tuplify(color))
]
rgbas: npt.NDArray[RGBA_Array_Float] = np.array(
[c.to_rgba_with_alpha(o) for c, o in zip(*make_even(colors, opacities))],
)
rgbas = np.array([c.to_rgba() for c in colors])

sheen_factor = self.get_sheen_factor()
if sheen_factor != 0 and len(rgbas) == 1:
Expand All @@ -252,10 +249,11 @@ def generate_rgbas_array(
def update_rgbas_array(
self,
array_name: str,
color: ManimColor | None = None,
opacity: float | None = None,
color: ManimColor | None,
) -> Self:
rgbas = self.generate_rgbas_array(color, opacity)
if color is None:
return self
rgbas = self.generate_rgbas_array(color)
if not hasattr(self, array_name):
setattr(self, array_name, rgbas)
return self
Expand All @@ -269,10 +267,7 @@ def update_rgbas_array(
rgbas = stretch_array_to_length(rgbas, len(curr_rgbas))
# Only update rgb if color was not None, and only
# update alpha channel if opacity was passed in
if color is not None:
curr_rgbas[:, :3] = rgbas[:, :3]
if opacity is not None:
curr_rgbas[:, 3] = rgbas[:, 3]
curr_rgbas[:, :4] = rgbas[:, :4]
return self

def set_fill(
Expand Down Expand Up @@ -316,23 +311,30 @@ def construct(self):
--------
:meth:`~.VMobject.set_style`
"""
if color is not None:
color = ManimColor.parse(color)
if opacity is not None:
color = color.opacity(opacity)

if family:
for submobject in self.submobjects:
submobject.set_fill(color, opacity, family)
self.update_rgbas_array("fill_rgbas", color, opacity)
self.update_rgbas_array("fill_rgbas", color)
self.fill_rgbas: RGBA_Array_Float
if opacity is not None:
self.fill_opacity = opacity
return self

def set_stroke(
self,
color: ParsableManimColor = None,
color: ParsableManimColor | None = None,
width: float | None = None,
opacity: float | None = None,
background=False,
family: bool = True,
) -> Self:
if color is not None:
color = ManimColor.parse(color)
if opacity is not None:
color = color.opacity(opacity)
if family:
for submobject in self.submobjects:
submobject.set_stroke(color, width, opacity, background, family)
Expand All @@ -344,7 +346,7 @@ def set_stroke(
array_name = "stroke_rgbas"
width_name = "stroke_width"
opacity_name = "stroke_opacity"
self.update_rgbas_array(array_name, color, opacity)
self.update_rgbas_array(array_name, color)
if width is not None:
setattr(self, width_name, width)
if opacity is not None:
Expand Down
35 changes: 34 additions & 1 deletion manim/utils/color/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,39 @@ def invert(self, with_alpha=False) -> ManimColor:
"""
return ManimColor(1.0 - self._internal_value, with_alpha)

@overload
def opacity(self, opacity: float) -> ManimColor:
"""Returns a new ManimColor with the same color and the given opacity

Parameters
----------
opacity : float
The opacity for the new ManimColor

Returns
-------
ManimColor
The new ManimColor object with changed opacity
"""

@overload
def opacity(self, opacity: None) -> float:
"""Returns the opacity of the current ManimColor in a range from zero to one

Returns
-------
float
The opacity of the ManimColor
"""

def opacity(self, opacity=None):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def opacity(self, opacity=None):
def opacity(self, opacity: float | None = None) -> ManimColor | float:

"""Returns a new ManimColor with the same color and a new opacity or changes the opacity"""
if opacity is None:
return self._internal_value[3]
tmp = self._internal_value.copy()
tmp[3] = opacity
return ManimColor.parse(tmp)
Comment on lines +634 to +665

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure about this overload. Could it be, instead, something like ManimColor.with_opacity(opacity) to return a new ManimColor with that different opacity, and ManimColor.get_opacity() or ManimColor.opacity as a property to get the current opacity?

Suggested change
@overload
def opacity(self, opacity: float) -> ManimColor:
"""Returns a new ManimColor with the same color and the given opacity
Parameters
----------
opacity : float
The opacity for the new ManimColor
Returns
-------
ManimColor
The new ManimColor object with changed opacity
"""
@overload
def opacity(self, opacity: None) -> float:
"""Returns the opacity of the current ManimColor in a range from zero to one
Returns
-------
float
The opacity of the ManimColor
"""
def opacity(self, opacity=None):
"""Returns a new ManimColor with the same color and a new opacity or changes the opacity"""
if opacity is None:
return self._internal_value[3]
tmp = self._internal_value.copy()
tmp[3] = opacity
return ManimColor.parse(tmp)
def with_opacity(self, opacity: float) -> ManimColor:
"""Returns a new ManimColor with the same color and the given opacity.
Parameters
----------
opacity
The opacity for the new ManimColor.
Returns
-------
ManimColor
The new ManimColor object with changed opacity.
"""
tmp = self._internal_value.copy()
tmp[3] = opacity
return ManimColor.parse(tmp)
@property
def opacity(self) -> float:
"""Returns the opacity of the current ManimColor, in a range from zero to one.
Returns
-------
float
The opacity of the ManimColor.
"""
return self._internal_value[3]


def interpolate(self, other: ManimColor, alpha: float) -> ManimColor:
"""Interpolates between the current and the given ManimColor an returns the interpolated color

Expand Down Expand Up @@ -676,7 +709,7 @@ def gradient(colors: list[ManimColor], length: int):
raise NotImplementedError

def __repr__(self) -> str:
return f"{self.__class__.__name__}('{self.to_hex()}')"
return f"{self.__class__.__name__}('{self.to_hex(True)}')"

def __str__(self) -> str:
return f"{self.to_hex()}"
Expand Down