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

1from math import sqrt 

2from functools import lru_cache 

3from typing import Sequence, Tuple, TYPE_CHECKING 

4 

5from .color_triplet import ColorTriplet 

6 

7if TYPE_CHECKING: 

8 from rich.table import Table 

9 

10 

11class Palette: 

12 """A palette of available colors.""" 

13 

14 def __init__(self, colors: Sequence[Tuple[int, int, int]]): 

15 self._colors = colors 

16 

17 def __getitem__(self, number: int) -> ColorTriplet: 

18 return ColorTriplet(*self._colors[number]) 

19 

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 

25 

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 

42 

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. 

47 

48 Args: 

49 color (Tuple[int, int, int]): RGB components in range 0 > 255. 

50 

51 Returns: 

52 int: Index of closes matching color. 

53 """ 

54 red1, green1, blue1 = color 

55 _sqrt = sqrt 

56 get_color = self._colors.__getitem__ 

57 

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 ) 

70 

71 min_index = min(range(len(self._colors)), key=get_color_distance) 

72 return min_index 

73 

74 

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 

82 

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() 

98 

99 console = Console() 

100 console.print(ColorBox())