Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
339 changes: 337 additions & 2 deletions manim/utils/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import random
import warnings
from enum import Enum, EnumMeta
from typing import Iterable

import numpy as np
Expand All @@ -30,6 +31,342 @@
from ..utils.bezier import interpolate
from ..utils.space_ops import normalize


class ColorsMeta(EnumMeta):
def __getattribute__(self, colorName):
colors = [
"white",
"gray_a",
"gray_b",
"gray_c",
"gray_d",
"gray_e",
"black",
"lighter_gray",
"gray",
"darker_gray",
"blue_a",
"blue_b",
"blue_c",
"blue_d",
"blue_e",
"pure_blue",
"blue",
"dark_blue",
"teal_a",
"teal_b",
"teal_c",
"teal_d",
"teal_e",
"teal",
"green_a",
"green_b",
"green_c",
"green_d",
"green_e",
"pure_green",
"green",
"yellow_a",
"yellow_b",
"yellow_c",
"yellow_d",
"yellow_e",
"yellow",
"gold_a",
"gold_b",
"gold_c",
"gold_d",
"gold_e",
"gold",
"red_a",
"red_b",
"red_c",
"red_d",
"red_e",
"pure_red",
"red",
"maroon_a",
"maroon_b",
"maroon_c",
"maroon_d",
"maroon_e",
"maroon",
"purple_a",
"purple_b",
"purple_c",
"purple_d",
"purple_e",
"purple",
"pink",
"light_pink",
"orange",
"light_brown",
"dark_brown",
"gray_brown",
]
if colorName in colors:
warnings.warn(
"Color enums is deprecated in favor of the constants. "
"The constants can be accessed by importing manim.utils.color",
DeprecationWarning,
stacklevel=2,
)
return EnumMeta.__getattribute__(self, colorName)


class Colors(Enum, metaclass=ColorsMeta):
"""A list of pre-defined colors.

Examples
--------

.. manim:: ColorsOverview
:save_last_frame:
:hide_source:

from manim.utils.color import Colors
class ColorsOverview(Scene):
def construct(self):
def color_group(color):
group = VGroup(
*[
Line(ORIGIN, RIGHT * 1.5, stroke_width=35, color=Colors[name].value)
for name in subnames(color)
]
).arrange_submobjects(buff=0.4, direction=DOWN)

name = Text(color).scale(0.6).next_to(group, UP, buff=0.3)
if any(decender in color for decender in "gjpqy"):
name.shift(DOWN * 0.08)
group.add(name)
return group

def subnames(name):
return [name + "_" + char for char in "abcde"]

color_groups = VGroup(
*[
color_group(color)
for color in [
"blue",
"teal",
"green",
"yellow",
"gold",
"red",
"maroon",
"purple",
]
]
).arrange_submobjects(buff=0.2, aligned_edge=DOWN)

for line, char in zip(color_groups[0], "abcde"):
color_groups.add(Text(char).scale(0.6).next_to(line, LEFT, buff=0.2))

def named_lines_group(length, colors, names, text_colors, align_to_block):
lines = VGroup(
*[
Line(
ORIGIN,
RIGHT * length,
stroke_width=55,
color=Colors[color].value,
)
for color in colors
]
).arrange_submobjects(buff=0.6, direction=DOWN)

for line, name, color in zip(lines, names, text_colors):
line.add(Text(name, color=color).scale(0.6).move_to(line))
lines.next_to(color_groups, DOWN, buff=0.5).align_to(
color_groups[align_to_block], LEFT
)
return lines

other_colors = (
"pink",
"light_pink",
"orange",
"light_brown",
"dark_brown",
"gray_brown",
)

other_lines = named_lines_group(
3.2,
other_colors,
other_colors,
[BLACK] * 4 + [WHITE] * 2,
0,
)

gray_lines = named_lines_group(
6.6,
["white"] + subnames("gray") + ["black"],
[
"white",
"lighter_gray / gray_a",
"light_gray / gray_b",
"gray / gray_c",
"dark_gray / gray_d",
"darker_gray / gray_e",
"black",
],
[BLACK] * 3 + [WHITE] * 4,
2,
)

pure_colors = (
"pure_red",
"pure_green",
"pure_blue",
)

pure_lines = named_lines_group(
3.2,
pure_colors,
pure_colors,
[BLACK, BLACK, WHITE],
6,
)

self.add(color_groups, other_lines, gray_lines, pure_lines)

VGroup(*self.mobjects).move_to(ORIGIN)


The preferred way of using these colors is by importing their constants from manim:

.. code-block:: pycon

>>> from manim import RED, GREEN, BLUE
>>> RED
'#FC6255'

Note this way uses the name of the colors in UPPERCASE.

Alternatively, you can also import this Enum directly and use its members
directly, through the use of :code:`color.value`. Note this way uses the
name of the colors in lowercase.

.. code-block:: pycon

>>> from manim.utils.color import Colors
>>> Colors.red.value
'#FC6255'

.. note::

The colors of type "C" have an alias equal to the colorname without a letter,
e.g. GREEN = GREEN_C

"""

white: str = "#FFFFFF"
gray_a: str = "#DDDDDD"
gray_b: str = "#BBBBBB"
gray_c: str = "#888888"
gray_d: str = "#444444"
gray_e: str = "#222222"
black: str = "#000000"
lighter_gray: str = gray_a
light_gray: str = gray_b
gray: str = gray_c
dark_gray: str = gray_d
darker_gray: str = gray_e

blue_a: str = "#C7E9F1"
blue_b: str = "#9CDCEB"
blue_c: str = "#58C4DD"
blue_d: str = "#29ABCA"
blue_e: str = "#236B8E"
pure_blue: str = "#0000FF"
blue: str = blue_c
dark_blue: str = blue_e

teal_a: str = "#ACEAD7"
teal_b: str = "#76DDC0"
teal_c: str = "#5CD0B3"
teal_d: str = "#55C1A7"
teal_e: str = "#49A88F"
teal: str = teal_c

green_a: str = "#C9E2AE"
green_b: str = "#A6CF8C"
green_c: str = "#83C167"
green_d: str = "#77B05D"
green_e: str = "#699C52"
pure_green: str = "#00FF00"
green: str = green_c

yellow_a: str = "#FFF1B6"
yellow_b: str = "#FFEA94"
yellow_c: str = "#FFFF00"
yellow_d: str = "#F4D345"
yellow_e: str = "#E8C11C"
yellow: str = yellow_c

gold_a: str = "#F7C797"
gold_b: str = "#F9B775"
gold_c: str = "#F0AC5F"
gold_d: str = "#E1A158"
gold_e: str = "#C78D46"
gold: str = gold_c

red_a: str = "#F7A1A3"
red_b: str = "#FF8080"
red_c: str = "#FC6255"
red_d: str = "#E65A4C"
red_e: str = "#CF5044"
pure_red: str = "#FF0000"
red: str = red_c

maroon_a: str = "#ECABC1"
maroon_b: str = "#EC92AB"
maroon_c: str = "#C55F73"
maroon_d: str = "#A24D61"
maroon_e: str = "#94424F"
maroon: str = maroon_c

purple_a: str = "#CAA3E8"
purple_b: str = "#B189C6"
purple_c: str = "#9A72AC"
purple_d: str = "#715582"
purple_e: str = "#644172"
purple: str = purple_c

pink: str = "#D147BD"
light_pink: str = "#DC75CD"

orange: str = "#FF862F"
light_brown: str = "#CD853F"
dark_brown: str = "#8B4513"
gray_brown: str = "#736357"


def print_constant_definitions():
"""
A simple function used to generate the constant values below. To run it
paste this function and the Colors class into a file and run them.
"""
constants_names: list[str] = []
for name in Colors.__members__.keys():
name_upper = name.upper()

constants_names.append(name_upper)
print(f"{name_upper} = Colors.{name}")

if "GRAY" in name_upper:
name_upper = name_upper.replace("GRAY", "GREY")

constants_names.append(name_upper)
print(f"{name_upper} = Colors.{name}")

constants_names_repr = '[\n "' + '",\n "'.join(constants_names) + '",\n]'

print(f"\n__all__ += {constants_names_repr}")


WHITE: Color = Color("#FFFFFF")
GRAY_A: Color = Color("#DDDDDD")
GREY_A: Color = Color("#DDDDDD")
Expand Down Expand Up @@ -282,7 +619,6 @@
]



def color_to_rgb(color: Color | str) -> np.ndarray:
warnings.warn(
"This method is not guaranteed to stay around. "
Expand Down Expand Up @@ -391,7 +727,6 @@ def random_color() -> Color:
return random.choice(list(ALL_COLORS.values()))



def get_shaded_rgb(
rgb: np.ndarray,
point: np.ndarray,
Expand Down