1
1
import matplotlib as mpl
2
2
import numpy as np
3
+ import webcolors
4
+
5
+ # RGB values (as taken from xcolor.dtx):
6
+ builtin_colors = {
7
+ # List white first such that for gray values, the combination
8
+ # white!<x>!black is preferred over, e.g., gray!<y>!black. Note that
9
+ # the order of the dictionary is respected from Python 3.6 on.
10
+ "white" : np .array ([1 , 1 , 1 ]),
11
+ "lightgray" : np .array ([0.75 , 0.75 , 0.75 ]),
12
+ "gray" : np .array ([0.5 , 0.5 , 0.5 ]),
13
+ "darkgray" : np .array ([0.25 , 0.25 , 0.25 ]),
14
+ "black" : np .array ([0 , 0 , 0 ]),
15
+ #
16
+ "red" : np .array ([1 , 0 , 0 ]),
17
+ "green" : np .array ([0 , 1 , 0 ]),
18
+ "blue" : np .array ([0 , 0 , 1 ]),
19
+ "brown" : np .array ([0.75 , 0.5 , 0.25 ]),
20
+ "lime" : np .array ([0.75 , 1 , 0 ]),
21
+ "orange" : np .array ([1 , 0.5 , 0 ]),
22
+ "pink" : np .array ([1 , 0.75 , 0.75 ]),
23
+ "purple" : np .array ([0.75 , 0 , 0.25 ]),
24
+ "teal" : np .array ([0 , 0.5 , 0.5 ]),
25
+ "violet" : np .array ([0.5 , 0 , 0.5 ]),
26
+ # The colors cyan, magenta, yellow, and olive are also
27
+ # predefined by xcolor, but their RGB approximation of the
28
+ # native CMYK values is not very good. Don't use them here.
29
+ }
30
+
31
+
32
+ def _get_closest_colour_name (rgb ):
33
+ match = None
34
+ mindiff = 1.0e15
35
+ for h , name in webcolors .CSS3_HEX_TO_NAMES .items ():
36
+ r = int (h [1 :3 ], 16 )
37
+ g = int (h [3 :5 ], 16 )
38
+ b = int (h [5 :7 ], 16 )
39
+
40
+ diff = (rgb [0 ] - r ) ** 2 + (rgb [1 ] - g ) ** 2 + (rgb [2 ] - b ) ** 2
41
+ if diff < mindiff :
42
+ match = name
43
+ mindiff = diff
44
+
45
+ if mindiff == 0 :
46
+ break
47
+
48
+ return match , mindiff
3
49
4
50
5
51
def mpl_color2xcolor (data , matplotlib_color ):
@@ -12,66 +58,28 @@ def mpl_color2xcolor(data, matplotlib_color):
12
58
if my_col [- 1 ] == 0.0 :
13
59
return data , "none" , my_col
14
60
15
- xcol = None
16
- # RGB values (as taken from xcolor.dtx):
17
- available_colors = {
18
- # List white first such that for gray values, the combination
19
- # white!<x>!black is preferred over, e.g., gray!<y>!black. Note that
20
- # the order of the dictionary is respected from Python 3.6 on.
21
- "white" : np .array ([1 , 1 , 1 ]),
22
- "lightgray" : np .array ([0.75 , 0.75 , 0.75 ]),
23
- "gray" : np .array ([0.5 , 0.5 , 0.5 ]),
24
- "darkgray" : np .array ([0.25 , 0.25 , 0.25 ]),
25
- "black" : np .array ([0 , 0 , 0 ]),
26
- #
27
- "red" : np .array ([1 , 0 , 0 ]),
28
- "green" : np .array ([0 , 1 , 0 ]),
29
- "blue" : np .array ([0 , 0 , 1 ]),
30
- "brown" : np .array ([0.75 , 0.5 , 0.25 ]),
31
- "lime" : np .array ([0.75 , 1 , 0 ]),
32
- "orange" : np .array ([1 , 0.5 , 0 ]),
33
- "pink" : np .array ([1 , 0.75 , 0.75 ]),
34
- "purple" : np .array ([0.75 , 0 , 0.25 ]),
35
- "teal" : np .array ([0 , 0.5 , 0.5 ]),
36
- "violet" : np .array ([0.5 , 0 , 0.5 ]),
37
- # The colors cyan, magenta, yellow, and olive are also
38
- # predefined by xcolor, but their RGB approximation of the
39
- # native CMYK values is not very good. Don't use them here.
40
- }
41
-
42
- available_colors .update (data ["custom colors" ])
43
-
44
61
# Check if it exactly matches any of the colors already available.
45
62
# This case is actually treated below (alpha==1), but that loop
46
63
# may pick up combinations with black before finding the exact
47
64
# match. Hence, first check all colors.
48
- for name , rgb in available_colors .items ():
65
+ for name , rgb in builtin_colors .items ():
49
66
if all (my_col [:3 ] == rgb ):
50
- xcol = name
51
- return data , xcol , my_col
52
-
53
- # Check if my_col is a multiple of a predefined color and 'black'.
54
- for name , rgb in available_colors .items ():
55
- if name == "black" :
56
- continue
67
+ return data , name , my_col
57
68
58
- if rgb [0 ] != 0.0 :
59
- alpha = my_col [ 0 ] / rgb [ 0 ]
60
- elif rgb [ 1 ] ! = 0.0 :
61
- alpha = my_col [1 ] / rgb [ 1 ]
62
- else :
63
- assert rgb [ 2 ] != 0.0
64
- alpha = my_col [ 2 ] / rgb [ 2 ]
69
+ if np . all ( my_col [0 ] == my_col [: 3 ]) :
70
+ # gray
71
+ # my_col[0] = 0.70123112 -> gray070
72
+ name = f"gray { int ( 100 * my_col [0 ]):03d } "
73
+ if name not in data [ "custom colors" ] :
74
+ data [ "custom colors" ][ name ] = ( "gray" , str ( my_col [ 0 ]))
75
+ return data , name , my_col
65
76
66
- # The cases 0.0 (my_col == black) and 1.0 (my_col == rgb) are
67
- # already accounted for by checking in available_colors above.
68
- if all (my_col [:3 ] == alpha * rgb ) and 0.0 < alpha < 1.0 :
69
- ff = data ["float format" ]
70
- xcol = name + f"!{ alpha * 100 :{ff }} !black"
71
- return data , xcol , my_col
77
+ # convert to RGB255
78
+ rgb255 = np .array (my_col [:3 ] * 255 , dtype = int )
72
79
73
- # Lookup failed, add it to the custom list.
74
- xcol = "color" + str (len (data ["custom colors" ]))
75
- data ["custom colors" ][xcol ] = my_col [:3 ]
80
+ name , diff = _get_closest_colour_name (rgb255 )
81
+ if diff > 0 :
82
+ name = f"{ name } { rgb255 [0 ]} { rgb255 [1 ]} { rgb255 [2 ]} "
83
+ data ["custom colors" ][name ] = ("RGB" , "," .join ([str (val ) for val in rgb255 ]))
76
84
77
- return data , xcol , my_col
85
+ return data , name , my_col
0 commit comments