1"""Tools for color handling in xonsh.
2
3This includes Convert values between RGB hex codes and xterm-256
4color codes. Parts of this file were originally forked from Micah Elliott
5http://MicahElliott.com Copyright (C) 2011 Micah Elliott. All rights reserved.
6WTFPL http://sam.zoy.org/wtfpl/
7"""
8import re
9import math
10
11from xonsh.lazyasd import lazyobject, LazyObject
12from xonsh.tools import deprecated
13
14
15RE_BACKGROUND = LazyObject(
16    lambda: re.compile("(BG#|BGHEX|BACKGROUND)"), globals(), "RE_BACKGROUND"
17)
18
19
20@lazyobject
21def BASE_XONSH_COLORS():
22    return {
23        "BLACK": (0, 0, 0),
24        "RED": (170, 0, 0),
25        "GREEN": (0, 170, 0),
26        "YELLOW": (170, 85, 0),
27        "BLUE": (0, 0, 170),
28        "PURPLE": (170, 0, 170),
29        "CYAN": (0, 170, 170),
30        "WHITE": (170, 170, 170),
31        "INTENSE_BLACK": (85, 85, 85),
32        "INTENSE_RED": (255, 85, 85),
33        "INTENSE_GREEN": (85, 255, 85),
34        "INTENSE_YELLOW": (255, 255, 85),
35        "INTENSE_BLUE": (85, 85, 255),
36        "INTENSE_PURPLE": (255, 85, 255),
37        "INTENSE_CYAN": (85, 255, 255),
38        "INTENSE_WHITE": (255, 255, 255),
39    }
40
41
42@lazyobject
43def CLUT():
44    """color look-up table"""
45    return [
46        #    8-bit, RGB hex
47        # Primary 3-bit (8 colors). Unique representation!
48        ("00", "000000"),
49        ("01", "800000"),
50        ("02", "008000"),
51        ("03", "808000"),
52        ("04", "000080"),
53        ("05", "800080"),
54        ("06", "008080"),
55        ("07", "c0c0c0"),
56        # Equivalent "bright" versions of original 8 colors.
57        ("08", "808080"),
58        ("09", "ff0000"),
59        ("10", "00ff00"),
60        ("11", "ffff00"),
61        ("12", "0000ff"),
62        ("13", "ff00ff"),
63        ("14", "00ffff"),
64        ("15", "ffffff"),
65        # Strictly ascending.
66        ("16", "000000"),
67        ("17", "00005f"),
68        ("18", "000087"),
69        ("19", "0000af"),
70        ("20", "0000d7"),
71        ("21", "0000ff"),
72        ("22", "005f00"),
73        ("23", "005f5f"),
74        ("24", "005f87"),
75        ("25", "005faf"),
76        ("26", "005fd7"),
77        ("27", "005fff"),
78        ("28", "008700"),
79        ("29", "00875f"),
80        ("30", "008787"),
81        ("31", "0087af"),
82        ("32", "0087d7"),
83        ("33", "0087ff"),
84        ("34", "00af00"),
85        ("35", "00af5f"),
86        ("36", "00af87"),
87        ("37", "00afaf"),
88        ("38", "00afd7"),
89        ("39", "00afff"),
90        ("40", "00d700"),
91        ("41", "00d75f"),
92        ("42", "00d787"),
93        ("43", "00d7af"),
94        ("44", "00d7d7"),
95        ("45", "00d7ff"),
96        ("46", "00ff00"),
97        ("47", "00ff5f"),
98        ("48", "00ff87"),
99        ("49", "00ffaf"),
100        ("50", "00ffd7"),
101        ("51", "00ffff"),
102        ("52", "5f0000"),
103        ("53", "5f005f"),
104        ("54", "5f0087"),
105        ("55", "5f00af"),
106        ("56", "5f00d7"),
107        ("57", "5f00ff"),
108        ("58", "5f5f00"),
109        ("59", "5f5f5f"),
110        ("60", "5f5f87"),
111        ("61", "5f5faf"),
112        ("62", "5f5fd7"),
113        ("63", "5f5fff"),
114        ("64", "5f8700"),
115        ("65", "5f875f"),
116        ("66", "5f8787"),
117        ("67", "5f87af"),
118        ("68", "5f87d7"),
119        ("69", "5f87ff"),
120        ("70", "5faf00"),
121        ("71", "5faf5f"),
122        ("72", "5faf87"),
123        ("73", "5fafaf"),
124        ("74", "5fafd7"),
125        ("75", "5fafff"),
126        ("76", "5fd700"),
127        ("77", "5fd75f"),
128        ("78", "5fd787"),
129        ("79", "5fd7af"),
130        ("80", "5fd7d7"),
131        ("81", "5fd7ff"),
132        ("82", "5fff00"),
133        ("83", "5fff5f"),
134        ("84", "5fff87"),
135        ("85", "5fffaf"),
136        ("86", "5fffd7"),
137        ("87", "5fffff"),
138        ("88", "870000"),
139        ("89", "87005f"),
140        ("90", "870087"),
141        ("91", "8700af"),
142        ("92", "8700d7"),
143        ("93", "8700ff"),
144        ("94", "875f00"),
145        ("95", "875f5f"),
146        ("96", "875f87"),
147        ("97", "875faf"),
148        ("98", "875fd7"),
149        ("99", "875fff"),
150        ("100", "878700"),
151        ("101", "87875f"),
152        ("102", "878787"),
153        ("103", "8787af"),
154        ("104", "8787d7"),
155        ("105", "8787ff"),
156        ("106", "87af00"),
157        ("107", "87af5f"),
158        ("108", "87af87"),
159        ("109", "87afaf"),
160        ("110", "87afd7"),
161        ("111", "87afff"),
162        ("112", "87d700"),
163        ("113", "87d75f"),
164        ("114", "87d787"),
165        ("115", "87d7af"),
166        ("116", "87d7d7"),
167        ("117", "87d7ff"),
168        ("118", "87ff00"),
169        ("119", "87ff5f"),
170        ("120", "87ff87"),
171        ("121", "87ffaf"),
172        ("122", "87ffd7"),
173        ("123", "87ffff"),
174        ("124", "af0000"),
175        ("125", "af005f"),
176        ("126", "af0087"),
177        ("127", "af00af"),
178        ("128", "af00d7"),
179        ("129", "af00ff"),
180        ("130", "af5f00"),
181        ("131", "af5f5f"),
182        ("132", "af5f87"),
183        ("133", "af5faf"),
184        ("134", "af5fd7"),
185        ("135", "af5fff"),
186        ("136", "af8700"),
187        ("137", "af875f"),
188        ("138", "af8787"),
189        ("139", "af87af"),
190        ("140", "af87d7"),
191        ("141", "af87ff"),
192        ("142", "afaf00"),
193        ("143", "afaf5f"),
194        ("144", "afaf87"),
195        ("145", "afafaf"),
196        ("146", "afafd7"),
197        ("147", "afafff"),
198        ("148", "afd700"),
199        ("149", "afd75f"),
200        ("150", "afd787"),
201        ("151", "afd7af"),
202        ("152", "afd7d7"),
203        ("153", "afd7ff"),
204        ("154", "afff00"),
205        ("155", "afff5f"),
206        ("156", "afff87"),
207        ("157", "afffaf"),
208        ("158", "afffd7"),
209        ("159", "afffff"),
210        ("160", "d70000"),
211        ("161", "d7005f"),
212        ("162", "d70087"),
213        ("163", "d700af"),
214        ("164", "d700d7"),
215        ("165", "d700ff"),
216        ("166", "d75f00"),
217        ("167", "d75f5f"),
218        ("168", "d75f87"),
219        ("169", "d75faf"),
220        ("170", "d75fd7"),
221        ("171", "d75fff"),
222        ("172", "d78700"),
223        ("173", "d7875f"),
224        ("174", "d78787"),
225        ("175", "d787af"),
226        ("176", "d787d7"),
227        ("177", "d787ff"),
228        ("178", "d7af00"),
229        ("179", "d7af5f"),
230        ("180", "d7af87"),
231        ("181", "d7afaf"),
232        ("182", "d7afd7"),
233        ("183", "d7afff"),
234        ("184", "d7d700"),
235        ("185", "d7d75f"),
236        ("186", "d7d787"),
237        ("187", "d7d7af"),
238        ("188", "d7d7d7"),
239        ("189", "d7d7ff"),
240        ("190", "d7ff00"),
241        ("191", "d7ff5f"),
242        ("192", "d7ff87"),
243        ("193", "d7ffaf"),
244        ("194", "d7ffd7"),
245        ("195", "d7ffff"),
246        ("196", "ff0000"),
247        ("197", "ff005f"),
248        ("198", "ff0087"),
249        ("199", "ff00af"),
250        ("200", "ff00d7"),
251        ("201", "ff00ff"),
252        ("202", "ff5f00"),
253        ("203", "ff5f5f"),
254        ("204", "ff5f87"),
255        ("205", "ff5faf"),
256        ("206", "ff5fd7"),
257        ("207", "ff5fff"),
258        ("208", "ff8700"),
259        ("209", "ff875f"),
260        ("210", "ff8787"),
261        ("211", "ff87af"),
262        ("212", "ff87d7"),
263        ("213", "ff87ff"),
264        ("214", "ffaf00"),
265        ("215", "ffaf5f"),
266        ("216", "ffaf87"),
267        ("217", "ffafaf"),
268        ("218", "ffafd7"),
269        ("219", "ffafff"),
270        ("220", "ffd700"),
271        ("221", "ffd75f"),
272        ("222", "ffd787"),
273        ("223", "ffd7af"),
274        ("224", "ffd7d7"),
275        ("225", "ffd7ff"),
276        ("226", "ffff00"),
277        ("227", "ffff5f"),
278        ("228", "ffff87"),
279        ("229", "ffffaf"),
280        ("230", "ffffd7"),
281        ("231", "ffffff"),
282        # Gray-scale range.
283        ("232", "080808"),
284        ("233", "121212"),
285        ("234", "1c1c1c"),
286        ("235", "262626"),
287        ("236", "303030"),
288        ("237", "3a3a3a"),
289        ("238", "444444"),
290        ("239", "4e4e4e"),
291        ("240", "585858"),
292        ("241", "626262"),
293        ("242", "6c6c6c"),
294        ("243", "767676"),
295        ("244", "808080"),
296        ("245", "8a8a8a"),
297        ("246", "949494"),
298        ("247", "9e9e9e"),
299        ("248", "a8a8a8"),
300        ("249", "b2b2b2"),
301        ("250", "bcbcbc"),
302        ("251", "c6c6c6"),
303        ("252", "d0d0d0"),
304        ("253", "dadada"),
305        ("254", "e4e4e4"),
306        ("255", "eeeeee"),
307    ]
308
309
310def _str2hex(hexstr):
311    return int(hexstr, 16)
312
313
314def _strip_hash(rgb):
315    # Strip leading `#` if exists.
316    if rgb.startswith("#"):
317        rgb = rgb.lstrip("#")
318    return rgb
319
320
321@lazyobject
322def SHORT_TO_RGB():
323    return dict(CLUT)
324
325
326@lazyobject
327def RGB_TO_SHORT():
328    return {v: k for k, v in SHORT_TO_RGB.items()}
329
330
331def short2rgb(short):
332    return SHORT_TO_RGB[short]
333
334
335def rgb_to_256(rgb):
336    """Find the closest ANSI 256 approximation to the given RGB value.
337
338        >>> rgb2short('123456')
339        ('23', '005f5f')
340        >>> rgb2short('ffffff')
341        ('231', 'ffffff')
342        >>> rgb2short('0DADD6') # vimeo logo
343        ('38', '00afd7')
344
345    Parameters
346    ----------
347    rgb : Hex code representing an RGB value, eg, 'abcdef'
348
349    Returns
350    -------
351    String between 0 and 255, compatible with xterm.
352    """
353    rgb = rgb.lstrip("#")
354    if len(rgb) == 0:
355        return "0", "000000"
356    incs = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
357    # Break 6-char RGB code into 3 integer vals.
358    parts = rgb_to_ints(rgb)
359    res = []
360    for part in parts:
361        i = 0
362        while i < len(incs) - 1:
363            s, b = incs[i], incs[i + 1]  # smaller, bigger
364            if s <= part <= b:
365                s1 = abs(s - part)
366                b1 = abs(b - part)
367                if s1 < b1:
368                    closest = s
369                else:
370                    closest = b
371                res.append(closest)
372                break
373            i += 1
374    res = "".join([("%02.x" % i) for i in res])
375    equiv = RGB_TO_SHORT[res]
376    return equiv, res
377
378
379rgb2short = rgb_to_256
380
381
382@lazyobject
383def RE_RGB3():
384    return re.compile(r"(.)(.)(.)")
385
386
387@lazyobject
388def RE_RGB6():
389    return re.compile(r"(..)(..)(..)")
390
391
392def rgb_to_ints(rgb):
393    if len(rgb) == 6:
394        return tuple([int(h, 16) for h in RE_RGB6.split(rgb)[1:4]])
395    else:
396        return tuple([int(h * 2, 16) for h in RE_RGB3.split(rgb)[1:4]])
397
398
399def color_dist(x, y):
400    return math.sqrt((x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2 + (x[2] - y[2]) ** 2)
401
402
403def find_closest_color(x, palette):
404    return min(sorted(palette.keys())[::-1], key=lambda k: color_dist(x, palette[k]))
405
406
407def make_palette(strings):
408    """Makes a color palette from a collection of strings."""
409    palette = {}
410    for s in strings:
411        while "#" in s:
412            _, t = s.split("#", 1)
413            t, _, s = t.partition(" ")
414            palette[t] = rgb_to_ints(t)
415    return palette
416
417
418@deprecated(deprecated_in="0.5.10", removed_in="0.6.0")
419def make_pallete(*args, **kwargs):
420    make_palette(*args, **kwargs)
421