Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/rich/palette.py: 35%
34 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:35 +0000
1from math import sqrt
2from functools import lru_cache
3from typing import Sequence, Tuple, TYPE_CHECKING
5from .color_triplet import ColorTriplet
7if TYPE_CHECKING:
8 from rich.table import Table
11class Palette:
12 """A palette of available colors."""
14 def __init__(self, colors: Sequence[Tuple[int, int, int]]):
15 self._colors = colors
17 def __getitem__(self, number: int) -> ColorTriplet:
18 return ColorTriplet(*self._colors[number])
20 def __rich__(self) -> "Table":
21 from rich.color import Color
22 from rich.style import Style
23 from rich.text import Text
24 from rich.table import Table
26 table = Table(
27 "index",
28 "RGB",
29 "Color",
30 title="Palette",
31 caption=f"{len(self._colors)} colors",
32 highlight=True,
33 caption_justify="right",
34 )
35 for index, color in enumerate(self._colors):
36 table.add_row(
37 str(index),
38 repr(color),
39 Text(" " * 16, style=Style(bgcolor=Color.from_rgb(*color))),
40 )
41 return table
43 # This is somewhat inefficient and needs caching
44 @lru_cache(maxsize=1024)
45 def match(self, color: Tuple[int, int, int]) -> int:
46 """Find a color from a palette that most closely matches a given color.
48 Args:
49 color (Tuple[int, int, int]): RGB components in range 0 > 255.
51 Returns:
52 int: Index of closes matching color.
53 """
54 red1, green1, blue1 = color
55 _sqrt = sqrt
56 get_color = self._colors.__getitem__
58 def get_color_distance(index: int) -> float:
59 """Get the distance to a color."""
60 red2, green2, blue2 = get_color(index)
61 red_mean = (red1 + red2) // 2
62 red = red1 - red2
63 green = green1 - green2
64 blue = blue1 - blue2
65 return _sqrt(
66 (((512 + red_mean) * red * red) >> 8)
67 + 4 * green * green
68 + (((767 - red_mean) * blue * blue) >> 8)
69 )
71 min_index = min(range(len(self._colors)), key=get_color_distance)
72 return min_index
75if __name__ == "__main__": # pragma: no cover
76 import colorsys
77 from typing import Iterable
78 from rich.color import Color
79 from rich.console import Console, ConsoleOptions
80 from rich.segment import Segment
81 from rich.style import Style
83 class ColorBox:
84 def __rich_console__(
85 self, console: Console, options: ConsoleOptions
86 ) -> Iterable[Segment]:
87 height = console.size.height - 3
88 for y in range(0, height):
89 for x in range(options.max_width):
90 h = x / options.max_width
91 l = y / (height + 1)
92 r1, g1, b1 = colorsys.hls_to_rgb(h, l, 1.0)
93 r2, g2, b2 = colorsys.hls_to_rgb(h, l + (1 / height / 2), 1.0)
94 bgcolor = Color.from_rgb(r1 * 255, g1 * 255, b1 * 255)
95 color = Color.from_rgb(r2 * 255, g2 * 255, b2 * 255)
96 yield Segment("▄", Style(color=color, bgcolor=bgcolor))
97 yield Segment.line()
99 console = Console()
100 console.print(ColorBox())