123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- from math import sqrt
- from functools import lru_cache
- from typing import Sequence, Tuple, TYPE_CHECKING
- from .color_triplet import ColorTriplet
- if TYPE_CHECKING:
- from pip._vendor.rich.table import Table
- class Palette:
- """A palette of available colors."""
- def __init__(self, colors: Sequence[Tuple[int, int, int]]):
- self._colors = colors
- def __getitem__(self, number: int) -> ColorTriplet:
- return ColorTriplet(*self._colors[number])
- def __rich__(self) -> "Table":
- from pip._vendor.rich.color import Color
- from pip._vendor.rich.style import Style
- from pip._vendor.rich.text import Text
- from pip._vendor.rich.table import Table
- table = Table(
- "index",
- "RGB",
- "Color",
- title="Palette",
- caption=f"{len(self._colors)} colors",
- highlight=True,
- caption_justify="right",
- )
- for index, color in enumerate(self._colors):
- table.add_row(
- str(index),
- repr(color),
- Text(" " * 16, style=Style(bgcolor=Color.from_rgb(*color))),
- )
- return table
- # This is somewhat inefficient and needs caching
- @lru_cache(maxsize=1024)
- def match(self, color: Tuple[int, int, int]) -> int:
- """Find a color from a palette that most closely matches a given color.
- Args:
- color (Tuple[int, int, int]): RGB components in range 0 > 255.
- Returns:
- int: Index of closes matching color.
- """
- red1, green1, blue1 = color
- _sqrt = sqrt
- get_color = self._colors.__getitem__
- def get_color_distance(index: int) -> float:
- """Get the distance to a color."""
- red2, green2, blue2 = get_color(index)
- red_mean = (red1 + red2) // 2
- red = red1 - red2
- green = green1 - green2
- blue = blue1 - blue2
- return _sqrt(
- (((512 + red_mean) * red * red) >> 8)
- + 4 * green * green
- + (((767 - red_mean) * blue * blue) >> 8)
- )
- min_index = min(range(len(self._colors)), key=get_color_distance)
- return min_index
- if __name__ == "__main__": # pragma: no cover
- import colorsys
- from typing import Iterable
- from pip._vendor.rich.color import Color
- from pip._vendor.rich.console import Console, ConsoleOptions
- from pip._vendor.rich.segment import Segment
- from pip._vendor.rich.style import Style
- class ColorBox:
- def __rich_console__(
- self, console: Console, options: ConsoleOptions
- ) -> Iterable[Segment]:
- height = console.size.height - 3
- for y in range(0, height):
- for x in range(options.max_width):
- h = x / options.max_width
- l = y / (height + 1)
- r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0)
- r2, g2, b2 = colorsys.hls_to_rgb(h, l + (1 / height / 2), 1.0)
- bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255)
- color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255)
- yield Segment("▄", Style(color=color, bgcolor=bgcolor))
- yield Segment.line()
- console = Console()
- console.print(ColorBox())
|