presets.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. from __future__ import annotations
  2. from typing_extensions import Literal
  3. from .color_util import RGB
  4. class ColorProfile:
  5. raw: list[str]
  6. colors: list[RGB]
  7. spacing: Literal['equal', 'weighted'] = 'equal'
  8. def __init__(self, colors: list[str] | list[RGB]):
  9. if isinstance(colors[0], str):
  10. self.raw = colors
  11. self.colors = [RGB.from_hex(c) for c in colors]
  12. else:
  13. self.colors = colors
  14. def with_weights(self, weights: list[int]) -> list[RGB]:
  15. """
  16. Map colors based on weights
  17. :param weights: Weights of each color (weights[i] = how many times color[i] appears)
  18. :return:
  19. """
  20. return [c for i, w in enumerate(weights) for c in [self.colors[i]] * w]
  21. def with_length(self, length: int) -> list[RGB]:
  22. """
  23. Spread to a specific length of text
  24. :param length: Length of text
  25. :return: List of RGBs of the length
  26. """
  27. preset_len = len(self.colors)
  28. center_i = preset_len // 2
  29. # How many copies of each color should be displayed at least?
  30. repeats = length // preset_len
  31. weights = [repeats] * preset_len
  32. # How many extra space left?
  33. extras = length % preset_len
  34. # If there is an even space left, extend the center by one space
  35. if extras % 2 == 1:
  36. extras -= 1
  37. weights[center_i] += 1
  38. # Add weight to border until there's no space left (extras must be even at this point)
  39. border_i = 0
  40. while extras > 0:
  41. extras -= 2
  42. weights[border_i] += 1
  43. weights[-(border_i + 1)] += 1
  44. border_i += 1
  45. return self.with_weights(weights)
  46. def color_text(self, txt: str, foreground: bool = True, space_only: bool = False) -> str:
  47. """
  48. Color a text
  49. :param txt: Text
  50. :param foreground: Whether the foreground text show the color or the background block
  51. :param space_only: Whether to only color spaces
  52. :return: Colored text
  53. """
  54. colors = self.with_length(len(txt))
  55. result = ''
  56. for i, t in enumerate(txt):
  57. if space_only and t != ' ':
  58. if i > 0 and txt[i - 1] == ' ':
  59. result += '\033[0m'
  60. result += t
  61. else:
  62. result += colors[i].to_ansi_rgb(foreground) + t
  63. result += '\033[0m'
  64. return result
  65. _C = ColorProfile # for convenience, must remove
  66. PRESETS: dict[str, ColorProfile] = {
  67. 'rainbow': ColorProfile([
  68. '#E50000',
  69. '#FF8D00',
  70. '#FFEE00',
  71. '#028121',
  72. '#004CFF',
  73. '#770088'
  74. ]),
  75. 'transgender': ColorProfile([
  76. '#55CDFD',
  77. '#F6AAB7',
  78. '#FFFFFF',
  79. '#F6AAB7',
  80. '#55CDFD'
  81. ]),
  82. 'nonbinary': ColorProfile([
  83. '#FCF431',
  84. '#FCFCFC',
  85. '#9D59D2',
  86. '#282828'
  87. ]),
  88. 'agender': ColorProfile([
  89. '#000000',
  90. '#BABABA',
  91. '#FFFFFF',
  92. '#BAF484',
  93. '#FFFFFF',
  94. '#BABABA',
  95. '#000000'
  96. ]),
  97. 'queer': ColorProfile([
  98. '#B57FDD',
  99. '#FFFFFF',
  100. '#49821E'
  101. ]),
  102. 'genderfluid': ColorProfile([
  103. '#FE76A2',
  104. '#FFFFFF',
  105. '#BF12D7',
  106. '#000000',
  107. '#303CBE'
  108. ]),
  109. 'bisexual': ColorProfile([
  110. '#D60270',
  111. '#9B4F96',
  112. '#0038A8'
  113. ]),
  114. 'pansexual': ColorProfile([
  115. '#FF1C8D',
  116. '#FFD700',
  117. '#1AB3FF'
  118. ]),
  119. 'lesbian': ColorProfile([
  120. '#D62800',
  121. '#FF9B56',
  122. '#FFFFFF',
  123. '#D462A6',
  124. '#A40062'
  125. ]),
  126. 'asexual': ColorProfile([
  127. '#000000',
  128. '#A4A4A4',
  129. '#FFFFFF',
  130. '#810081'
  131. ]),
  132. 'aromantic': ColorProfile([
  133. '#3BA740',
  134. '#A8D47A',
  135. '#FFFFFF',
  136. '#ABABAB',
  137. '#000000'
  138. ]),
  139. # below sourced from https://www.flagcolorcodes.com/flags/pride
  140. # goto f"https://www.flagcolorcodes.com/{preset}" for info
  141. # todo: sane sorting
  142. 'autosexual': _C([
  143. '#99D9EA',
  144. '#7F7F7F'
  145. ]),
  146. 'intergender': _C([
  147. # todo: implement weighted spacing
  148. '#900DC2',
  149. '#900DC2',
  150. '#FFE54F',
  151. '#900DC2',
  152. '#900DC2',
  153. ]),
  154. 'greygender': _C([
  155. '#B3B3B3',
  156. '#B3B3B3',
  157. '#FFFFFF',
  158. '#062383',
  159. '#062383',
  160. '#FFFFFF',
  161. '#535353',
  162. '#535353',
  163. ]),
  164. 'akiosexual': _C([
  165. '#F9485E',
  166. '#FEA06A',
  167. '#FEF44C',
  168. '#FFFFFF',
  169. '#000000',
  170. ]),
  171. 'transmasculine': _C([
  172. '#FF8ABD',
  173. '#CDF5FE',
  174. '#9AEBFF',
  175. '#74DFFF',
  176. '#9AEBFF',
  177. '#CDF5FE',
  178. '#FF8ABD',
  179. ]),
  180. 'demifaun': _C([
  181. '#7F7F7F',
  182. '#7F7F7F',
  183. '#C6C6C6',
  184. '#C6C6C6',
  185. '#FCC688',
  186. '#FFF19C',
  187. '#FFFFFF',
  188. '#8DE0D5',
  189. '#9682EC',
  190. '#C6C6C6',
  191. '#C6C6C6',
  192. '#7F7F7F',
  193. '#7F7F7F',
  194. ]),
  195. 'neutrois': _C([
  196. '#FFFFFF',
  197. '#1F9F00',
  198. '#000000'
  199. ]),
  200. 'biromantic alt 2': _C([
  201. '#8869A5',
  202. '#D8A7D8',
  203. '#FFFFFF',
  204. '#FDB18D',
  205. '#151638',
  206. ]),
  207. 'biromantic alt 2': _C([
  208. '#740194',
  209. '#AEB1AA',
  210. '#FFFFFF',
  211. '#AEB1AA',
  212. '#740194',
  213. ]),
  214. 'autoromantic': _C([ # symbol interpreted
  215. '#99D9EA',
  216. '#99D9EA',
  217. '#99D9EA',
  218. '#99D9EA',
  219. '#99D9EA',
  220. '#000000',
  221. '#3DA542',
  222. '#3DA542',
  223. '#000000',
  224. '#7F7F7F',
  225. '#7F7F7F',
  226. '#7F7F7F',
  227. '#7F7F7F',
  228. '#7F7F7F',
  229. ]),
  230. # i didn't expect this one to work. cool!
  231. 'boyflux alt 2': _C([
  232. '#E48AE4',
  233. '#9A81B4',
  234. '#55BFAB',
  235. '#FFFFFF',
  236. '#A8A8A8',
  237. '#81D5EF',
  238. '#81D5EF',
  239. '#81D5EF',
  240. '#81D5EF',
  241. '#81D5EF',
  242. '#69ABE5',
  243. '#69ABE5',
  244. '#69ABE5',
  245. '#69ABE5',
  246. '#69ABE5',
  247. '#69ABE5',
  248. '#69ABE5',
  249. '#69ABE5',
  250. '#69ABE5',
  251. '#69ABE5',
  252. '#5276D4',
  253. '#5276D4',
  254. '#5276D4',
  255. '#5276D4',
  256. '#5276D4',
  257. '#5276D4',
  258. '#5276D4',
  259. '#5276D4',
  260. '#5276D4',
  261. '#5276D4',
  262. ]),
  263. }