11import matplotlib as mpl
22import numpy as np
3+ import webcolors
4+
5+ # RGB values (as taken from xcolor.dtx):
6+ builtin_colors = {
7+ "white" : [1 , 1 , 1 ],
8+ "lightgray" : [0.75 , 0.75 , 0.75 ],
9+ "gray" : [0.5 , 0.5 , 0.5 ],
10+ "darkgray" : [0.25 , 0.25 , 0.25 ],
11+ "black" : [0 , 0 , 0 ],
12+ #
13+ "red" : [1 , 0 , 0 ],
14+ "green" : [0 , 1 , 0 ],
15+ "blue" : [0 , 0 , 1 ],
16+ "brown" : [0.75 , 0.5 , 0.25 ],
17+ "lime" : [0.75 , 1 , 0 ],
18+ "orange" : [1 , 0.5 , 0 ],
19+ "pink" : [1 , 0.75 , 0.75 ],
20+ "purple" : [0.75 , 0 , 0.25 ],
21+ "teal" : [0 , 0.5 , 0.5 ],
22+ "violet" : [0.5 , 0 , 0.5 ],
23+ # The colors cyan, magenta, yellow, and olive are also
24+ # predefined by xcolor, but their RGB approximation of the
25+ # native CMYK values is not very good. Don't use them here.
26+ }
27+
28+
29+ def _get_closest_colour_name (rgb ):
30+ match = None
31+ mindiff = 1.0e15
32+ for h , name in webcolors .CSS3_HEX_TO_NAMES .items ():
33+ r = int (h [1 :3 ], 16 )
34+ g = int (h [3 :5 ], 16 )
35+ b = int (h [5 :7 ], 16 )
36+
37+ diff = (rgb [0 ] - r ) ** 2 + (rgb [1 ] - g ) ** 2 + (rgb [2 ] - b ) ** 2
38+ if diff < mindiff :
39+ match = name
40+ mindiff = diff
41+
42+ if mindiff == 0 :
43+ break
44+
45+ return match , mindiff
346
447
548def mpl_color2xcolor (data , matplotlib_color ):
@@ -12,66 +55,30 @@ def mpl_color2xcolor(data, matplotlib_color):
1255 if my_col [- 1 ] == 0.0 :
1356 return data , "none" , my_col
1457
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-
4458 # Check if it exactly matches any of the colors already available.
4559 # This case is actually treated below (alpha==1), but that loop
4660 # may pick up combinations with black before finding the exact
4761 # match. Hence, first check all colors.
48- for name , rgb in available_colors .items ():
62+ for name , rgb in builtin_colors .items ():
4963 if all (my_col [:3 ] == rgb ):
50- xcol = name
51- return data , xcol , my_col
64+ return data , name , my_col
5265
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
66+ # Don't handle gray colors separately. They can be specified in xcolor as
67+ #
68+ # {gray}{0.6901960784313725}
69+ #
70+ # but this float representation hides the fact that this is actually an
71+ # RGB255 integer value, 176.
5772
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 ]
73+ # convert to RGB255
74+ rgb255 = np .array (my_col [:3 ] * 255 , dtype = int )
6575
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
72-
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 ]
76+ name , diff = _get_closest_colour_name (rgb255 )
77+ if diff > 0 :
78+ if np .all (my_col [0 ] == my_col [:3 ]):
79+ name = f"{ name } { rgb255 [0 ]} "
80+ else :
81+ name = f"{ name } { rgb255 [0 ]} { rgb255 [1 ]} { rgb255 [2 ]} "
82+ data ["custom colors" ][name ] = ("RGB" , "," .join ([str (val ) for val in rgb255 ]))
7683
77- return data , xcol , my_col
84+ return data , name , my_col
0 commit comments