Coverage for /run/media/veganeco/water/status600.com/pypi/reptilian_climates/modules_series_2/ships/fields/gardens_pip/rich/palette.py: 35%

1 from math import sqrt

2 from functools import lru_cache

3 from typing import Sequence, Tuple, TYPE_CHECKING

4

5 from .color_triplet import ColorTriplet

6

7 if TYPE_CHECKING:

8 from rich.table import Table

9

10

11 class 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

75 if __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())