Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/plot/colors.py
4034 views
1
"""
2
Colors
3
4
This module defines a :class:`Color` object and helper functions (see,
5
e.g., :func:`hue`, :func:`rainbow`), as well as a set of
6
:data:`colors` and :data:`colormaps` to use with
7
:class:`Graphics` objects in Sage.
8
9
For a list of pre-defined colors in Sage, evaluate::
10
11
sage: sorted(colors)
12
['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'automatic', ...]
13
14
Apart from 'automatic' which just an alias for 'lightblue', this list
15
comprises the "official" W3C CSS3_ / SVG_ colors.
16
17
.. _CSS3: http://www.w3.org/TR/css3-color/#svg-color
18
.. _SVG: http://www.w3.org/TR/SVG/types.html#ColorKeywords
19
20
For a list of color maps in Sage, evaluate::
21
22
sage: sorted(colormaps)
23
['Accent', 'Accent_r', 'Blues', 'Blues_r', 'BrBG', 'BrBG_r', ...]
24
25
These are imported from matplotlib's cm_ module.
26
27
.. _cm: http://matplotlib.sourceforge.net/api/cm_api.html
28
"""
29
30
#*****************************************************************************
31
# Distributed under the terms of the GNU General Public License (GPL)
32
#
33
# This code is distributed in the hope that it will be useful,
34
# but WITHOUT ANY WARRANTY; without even the implied warranty of
35
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
36
# General Public License for more details.
37
#
38
# The full text of the GPL is available at:
39
#
40
# http://www.gnu.org/licenses/
41
#*****************************************************************************
42
43
import math
44
import collections
45
from colorsys import hsv_to_rgb, hls_to_rgb, rgb_to_hsv, rgb_to_hls
46
47
# matplotlib color maps, loaded on-demand
48
cm = None
49
50
51
colors_dict = {
52
'automatic' : '#add8e6', # 173, 216, 230
53
'aliceblue' : '#f0f8ff', # 240, 248, 255
54
'antiquewhite' : '#faebd7', # 250, 235, 215
55
'aqua' : '#00ffff', # 0, 255, 255
56
'aquamarine' : '#7fffd4', # 127, 255, 212
57
'azure' : '#f0ffff', # 240, 255, 255
58
'beige' : '#f5f5dc', # 245, 245, 220
59
'bisque' : '#ffe4c4', # 255, 228, 196
60
'black' : '#000000', # 0, 0, 0
61
'blanchedalmond' : '#ffebcd', # 255, 235, 205
62
'blue' : '#0000ff', # 0, 0, 255
63
'blueviolet' : '#8a2be2', # 138, 43, 226
64
'brown' : '#a52a2a', # 165, 42, 42
65
'burlywood' : '#deb887', # 222, 184, 135
66
'cadetblue' : '#5f9ea0', # 95, 158, 160
67
'chartreuse' : '#7fff00', # 127, 255, 0
68
'chocolate' : '#d2691e', # 210, 105, 30
69
'coral' : '#ff7f50', # 255, 127, 80
70
'cornflowerblue' : '#6495ed', # 100, 149, 237
71
'cornsilk' : '#fff8dc', # 255, 248, 220
72
'crimson' : '#dc143c', # 220, 20, 60
73
'cyan' : '#00ffff', # 0, 255, 255
74
'darkblue' : '#00008b', # 0, 0, 139
75
'darkcyan' : '#008b8b', # 0, 139, 139
76
'darkgoldenrod' : '#b8860b', # 184, 134, 11
77
'darkgray' : '#a9a9a9', # 169, 169, 169
78
'darkgreen' : '#006400', # 0, 100, 0
79
'darkgrey' : '#a9a9a9', # 169, 169, 169
80
'darkkhaki' : '#bdb76b', # 189, 183, 107
81
'darkmagenta' : '#8b008b', # 139, 0, 139
82
'darkolivegreen' : '#556b2f', # 85, 107, 47
83
'darkorange' : '#ff8c00', # 255, 140, 0
84
'darkorchid' : '#9932cc', # 153, 50, 204
85
'darkred' : '#8b0000', # 139, 0, 0
86
'darksalmon' : '#e9967a', # 233, 150, 122
87
'darkseagreen' : '#8fbc8f', # 143, 188, 143
88
'darkslateblue' : '#483d8b', # 72, 61, 139
89
'darkslategray' : '#2f4f4f', # 47, 79, 79
90
'darkslategrey' : '#2f4f4f', # 47, 79, 79
91
'darkturquoise' : '#00ced1', # 0, 206, 209
92
'darkviolet' : '#9400d3', # 148, 0, 211
93
'deeppink' : '#ff1493', # 255, 20, 147
94
'deepskyblue' : '#00bfff', # 0, 191, 255
95
'dimgray' : '#696969', # 105, 105, 105
96
'dimgrey' : '#696969', # 105, 105, 105
97
'dodgerblue' : '#1e90ff', # 30, 144, 255
98
'firebrick' : '#b22222', # 178, 34, 34
99
'floralwhite' : '#fffaf0', # 255, 250, 240
100
'forestgreen' : '#228b22', # 34, 139, 34
101
'fuchsia' : '#ff00ff', # 255, 0, 255
102
'gainsboro' : '#dcdcdc', # 220, 220, 220
103
'ghostwhite' : '#f8f8ff', # 248, 248, 255
104
'gold' : '#ffd700', # 255, 215, 0
105
'goldenrod' : '#daa520', # 218, 165, 32
106
'gray' : '#808080', # 128, 128, 128
107
'green' : '#008000', # 0, 128, 0
108
'greenyellow' : '#adff2f', # 173, 255, 47
109
'grey' : '#808080', # 128, 128, 128
110
'honeydew' : '#f0fff0', # 240, 255, 240
111
'hotpink' : '#ff69b4', # 255, 105, 180
112
'indianred' : '#cd5c5c', # 205, 92, 92
113
'indigo' : '#4b0082', # 75, 0, 130
114
'ivory' : '#fffff0', # 255, 255, 240
115
'khaki' : '#f0e68c', # 240, 230, 140
116
'lavender' : '#e6e6fa', # 230, 230, 250
117
'lavenderblush' : '#fff0f5', # 255, 240, 245
118
'lawngreen' : '#7cfc00', # 124, 252, 0
119
'lemonchiffon' : '#fffacd', # 255, 250, 205
120
'lightblue' : '#add8e6', # 173, 216, 230
121
'lightcoral' : '#f08080', # 240, 128, 128
122
'lightcyan' : '#e0ffff', # 224, 255, 255
123
'lightgoldenrodyellow' : '#fafad2', # 250, 250, 210
124
'lightgray' : '#d3d3d3', # 211, 211, 211
125
'lightgreen' : '#90ee90', # 144, 238, 144
126
'lightgrey' : '#d3d3d3', # 211, 211, 211
127
'lightpink' : '#ffb6c1', # 255, 182, 193
128
'lightsalmon' : '#ffa07a', # 255, 160, 122
129
'lightseagreen' : '#20b2aa', # 32, 178, 170
130
'lightskyblue' : '#87cefa', # 135, 206, 250
131
'lightslategray' : '#778899', # 119, 136, 153
132
'lightslategrey' : '#778899', # 119, 136, 153
133
'lightsteelblue' : '#b0c4de', # 176, 196, 222
134
'lightyellow' : '#ffffe0', # 255, 255, 224
135
'lime' : '#00ff00', # 0, 255, 0
136
'limegreen' : '#32cd32', # 50, 205, 50
137
'linen' : '#faf0e6', # 250, 240, 230
138
'magenta' : '#ff00ff', # 255, 0, 255
139
'maroon' : '#800000', # 128, 0, 0
140
'mediumaquamarine' : '#66cdaa', # 102, 205, 170
141
'mediumblue' : '#0000cd', # 0, 0, 205
142
'mediumorchid' : '#ba55d3', # 186, 85, 211
143
'mediumpurple' : '#9370db', # 147, 112, 219
144
'mediumseagreen' : '#3cb371', # 60, 179, 113
145
'mediumslateblue' : '#7b68ee', # 123, 104, 238
146
'mediumspringgreen' : '#00fa9a', # 0, 250, 154
147
'mediumturquoise' : '#48d1cc', # 72, 209, 204
148
'mediumvioletred' : '#c71585', # 199, 21, 133
149
'midnightblue' : '#191970', # 25, 25, 112
150
'mintcream' : '#f5fffa', # 245, 255, 250
151
'mistyrose' : '#ffe4e1', # 255, 228, 225
152
'moccasin' : '#ffe4b5', # 255, 228, 181
153
'navajowhite' : '#ffdead', # 255, 222, 173
154
'navy' : '#000080', # 0, 0, 128
155
'oldlace' : '#fdf5e6', # 253, 245, 230
156
'olive' : '#808000', # 128, 128, 0
157
'olivedrab' : '#6b8e23', # 107, 142, 35
158
'orange' : '#ffa500', # 255, 165, 0
159
'orangered' : '#ff4500', # 255, 69, 0
160
'orchid' : '#da70d6', # 218, 112, 214
161
'palegoldenrod' : '#eee8aa', # 238, 232, 170
162
'palegreen' : '#98fb98', # 152, 251, 152
163
'paleturquoise' : '#afeeee', # 175, 238, 238
164
'palevioletred' : '#db7093', # 219, 112, 147
165
'papayawhip' : '#ffefd5', # 255, 239, 213
166
'peachpuff' : '#ffdab9', # 255, 218, 185
167
'peru' : '#cd853f', # 205, 133, 63
168
'pink' : '#ffc0cb', # 255, 192, 203
169
'plum' : '#dda0dd', # 221, 160, 221
170
'powderblue' : '#b0e0e6', # 176, 224, 230
171
'purple' : '#800080', # 128, 0, 128
172
'red' : '#ff0000', # 255, 0, 0
173
'rosybrown' : '#bc8f8f', # 188, 143, 143
174
'royalblue' : '#4169e1', # 65, 105, 225
175
'saddlebrown' : '#8b4513', # 139, 69, 19
176
'salmon' : '#fa8072', # 250, 128, 114
177
'sandybrown' : '#f4a460', # 244, 164, 96
178
'seagreen' : '#2e8b57', # 46, 139, 87
179
'seashell' : '#fff5ee', # 255, 245, 238
180
'sienna' : '#a0522d', # 160, 82, 45
181
'silver' : '#c0c0c0', # 192, 192, 192
182
'skyblue' : '#87ceeb', # 135, 206, 235
183
'slateblue' : '#6a5acd', # 106, 90, 205
184
'slategray' : '#708090', # 112, 128, 144
185
'slategrey' : '#708090', # 112, 128, 144
186
'snow' : '#fffafa', # 255, 250, 250
187
'springgreen' : '#00ff7f', # 0, 255, 127
188
'steelblue' : '#4682b4', # 70, 130, 180
189
'tan' : '#d2b48c', # 210, 180, 140
190
'teal' : '#008080', # 0, 128, 128
191
'thistle' : '#d8bfd8', # 216, 191, 216
192
'tomato' : '#ff6347', # 255, 99, 71
193
'turquoise' : '#40e0d0', # 64, 224, 208
194
'violet' : '#ee82ee', # 238, 130, 238
195
'wheat' : '#f5deb3', # 245, 222, 179
196
'white' : '#ffffff', # 255, 255, 255
197
'whitesmoke' : '#f5f5f5', # 245, 245, 245
198
'yellow' : '#ffff00', # 255, 255, 0
199
'yellowgreen' : '#9acd32' # 154, 205, 50
200
}
201
202
203
def mod_one(x):
204
"""
205
Reduce a number modulo 1.
206
207
INPUT:
208
209
- ``x`` - an instance of Integer, int, RealNumber, etc.; the
210
number to reduce
211
212
OUTPUT:
213
214
- a float
215
216
EXAMPLES::
217
218
sage: from sage.plot.colors import mod_one
219
sage: mod_one(1)
220
1.0
221
sage: mod_one(7.0)
222
0.0
223
sage: mod_one(-11/7)
224
0.4285714285714286
225
sage: mod_one(pi) + mod_one(-pi)
226
1.0
227
"""
228
x = float(x)
229
if x != 1:
230
x = math.modf(x)[0]
231
if x < 0:
232
x += 1
233
return x
234
235
236
def html_to_float(c):
237
"""
238
Convert a HTML hex color to a Red-Green-Blue (RGB) tuple.
239
240
INPUT:
241
242
- ``c`` - a string; a valid HTML hex color
243
244
OUTPUT:
245
246
- a RGB 3-tuple of floats in the interval [0.0, 1.0]
247
248
EXAMPLES::
249
250
sage: from sage.plot.colors import html_to_float
251
sage: html_to_float('#fff')
252
(1.0, 1.0, 1.0)
253
sage: html_to_float('#abcdef')
254
(0.6705882352941176, 0.803921568627451, 0.9372549019607843)
255
sage: html_to_float('#123xyz')
256
Traceback (most recent call last):
257
...
258
ValueError: invalid literal for int() with base 16: '3x'
259
"""
260
if not len(c) or c[0] != '#':
261
raise ValueError("'%s' must be a valid HTML hex color (e.g., '#f07' or '#d6e7da')" % c)
262
h = c[1:]
263
if len(h) == 3:
264
h = '%s%s%s%s%s%s' % (h[0], h[0], h[1], h[1], h[2], h[2])
265
elif len(h) != 6:
266
raise ValueError("color hex string (= '%s') must have length 3 or 6" % h)
267
return tuple([int(h[i:i + 2], base=16) / float(255) for i in [0, 2, 4]])
268
269
270
def rgbcolor(c, space='rgb'):
271
"""
272
Convert a color (string, tuple, list, or :class:`Color`) to a
273
mod-one reduced (see :func:`mod_one`) valid Red-Green-Blue (RGB)
274
tuple. The returned tuple is also a valid matplotlib RGB color.
275
276
INPUT:
277
278
- ``c`` - a :class:`Color` instance, string (name or HTML hex),
279
3-tuple, or 3-list; the color to convert
280
281
- ``space`` - a string (default: 'rgb'); the color space
282
coordinate system (other choices are 'hsl', 'hls', and 'hsv') in
283
which to interpret a 3-tuple or 3-list
284
285
OUTPUT:
286
287
- a RGB 3-tuple of floats in the interval [0.0, 1.0]
288
289
EXAMPLES::
290
291
sage: from sage.plot.colors import rgbcolor
292
sage: rgbcolor(Color(0.25, 0.4, 0.9))
293
(0.25, 0.4, 0.9)
294
sage: rgbcolor('purple')
295
(0.5019607843137255, 0.0, 0.5019607843137255)
296
sage: rgbcolor('#fa0')
297
(1.0, 0.6666666666666666, 0.0)
298
sage: rgbcolor('#ffffff')
299
(1.0, 1.0, 1.0)
300
sage: rgbcolor((1,1/2,1/3))
301
(1.0, 0.5, 0.3333333333333333)
302
sage: rgbcolor([1,1/2,1/3])
303
(1.0, 0.5, 0.3333333333333333)
304
sage: rgbcolor((1,1,1), space='hsv')
305
(1.0, 0.0, 0.0)
306
sage: rgbcolor((0.5,0.75,1), space='hls')
307
(0.5, 0.9999999999999999, 1.0)
308
sage: rgbcolor((0.5,1.0,0.75), space='hsl')
309
(0.5, 0.9999999999999999, 1.0)
310
sage: rgbcolor([1,2,255]) # WARNING -- numbers are reduced mod 1!!
311
(1.0, 0.0, 0.0)
312
sage: rgbcolor('#abcd')
313
Traceback (most recent call last):
314
...
315
ValueError: color hex string (= 'abcd') must have length 3 or 6
316
sage: rgbcolor('fff')
317
Traceback (most recent call last):
318
...
319
ValueError: unknown color 'fff'
320
sage: rgbcolor(1)
321
Traceback (most recent call last):
322
...
323
TypeError: '1' must be a Color, list, tuple, or string
324
sage: rgbcolor((0.2,0.8,1), space='grassmann')
325
Traceback (most recent call last):
326
...
327
ValueError: space must be one of 'rgb', 'hsv', 'hsl', 'hls'
328
sage: rgbcolor([0.4, 0.1])
329
Traceback (most recent call last):
330
...
331
ValueError: color list or tuple '[0.400000000000000, 0.100000000000000]' must have 3 entries, one for each RGB, HSV, HLS, or HSL channel
332
"""
333
if isinstance(c, Color):
334
return c.rgb()
335
336
if isinstance(c, str):
337
if len(c) > 0 and c[0] == '#':
338
# Assume an HTML-like color, e.g., #00ffff or #ab0.
339
return html_to_float(c)
340
else:
341
try:
342
return colors[c].rgb()
343
except KeyError:
344
raise ValueError("unknown color '%s'" % c)
345
346
elif isinstance(c, (list, tuple)):
347
if len(c) != 3:
348
raise ValueError("color list or tuple '%s' must have 3 entries, one for each RGB, HSV, HLS, or HSL channel" % (c, ))
349
c = map(mod_one, list(c))
350
if space == 'rgb':
351
return tuple(c)
352
elif space == 'hsv':
353
return tuple(map(float, hsv_to_rgb(*c)))
354
elif space == 'hls':
355
return tuple(map(float, hls_to_rgb(*c)))
356
elif space == 'hsl':
357
return tuple(map(float, hls_to_rgb(c[0], c[2], c[1])))
358
else:
359
raise ValueError("space must be one of 'rgb', 'hsv', 'hsl', 'hls'")
360
361
raise TypeError("'%s' must be a Color, list, tuple, or string" % c)
362
363
364
# For backward compatibility.
365
to_mpl_color = rgbcolor
366
367
368
class Color(object):
369
def __init__(self, r='#0000ff', g=None, b=None, space='rgb'):
370
"""
371
An Red-Green-Blue (RGB) color model color object. For most
372
consumer-grade devices (e.g., CRTs, LCDs, and printers), as
373
well as internet applications, this is a point in the sRGB
374
absolute color space. The Hue-Saturation-Lightness (HSL),
375
Hue-Lightness-Saturation (HLS), and Hue-Saturation-Value (HSV)
376
spaces are useful alternate representations, or coordinate
377
transformations, of this space. Coordinates in all of these
378
spaces are floating point values in the interval [0.0, 1.0].
379
380
.. note:: All instantiations of :class:`Color` are converted
381
to an internal RGB floating point 3-tuple. This is
382
likely to degrade precision.
383
384
INPUT:
385
386
- ``r,g,b`` - either a triple of floats between 0 and 1,
387
OR ``r`` - a color name string or HTML color hex string
388
389
- ``space`` - a string (default: 'rgb'); the coordinate system
390
(other choices are 'hsl', 'hls', and 'hsv') in which to
391
interpret a triple of floats
392
393
EXAMPLES::
394
395
sage: Color('purple')
396
RGB color (0.5019607843137255, 0.0, 0.5019607843137255)
397
sage: Color('#8000ff')
398
RGB color (0.5019607843137255, 0.0, 1.0)
399
sage: Color(0.5,0,1)
400
RGB color (0.5, 0.0, 1.0)
401
sage: Color(0.5, 1.0, 1, space='hsv')
402
RGB color (0.0, 1.0, 1.0)
403
sage: Color(0.25, 0.5, 0.5, space='hls')
404
RGB color (0.5000000000000001, 0.75, 0.25)
405
sage: Color(1, 0, 1/3, space='hsl')
406
RGB color (0.3333333333333333, 0.3333333333333333, 0.3333333333333333)
407
sage: from sage.plot.colors import chocolate
408
sage: Color(chocolate)
409
RGB color (0.8235294117647058, 0.4117647058823529, 0.11764705882352941)
410
"""
411
if g is None and b is None:
412
self.__rgb = rgbcolor(r)
413
else:
414
self.__rgb = rgbcolor((r, g, b), space=space)
415
416
def __repr__(self):
417
"""
418
Return a string representation of this color.
419
420
OUTPUT:
421
422
- a string
423
424
EXAMPLES::
425
426
sage: Color('#8000ff').__repr__()
427
'RGB color (0.5019607843137255, 0.0, 1.0)'
428
sage: Color(1, 0.5, 1/16, space='hsl').__repr__()
429
'RGB color (0.09375, 0.03125, 0.03125)'
430
"""
431
return "RGB color %s" % (self.__rgb, )
432
433
def __lt__(self, right):
434
"""
435
Check whether a :class:`Color` object is less than some other
436
object. This doesn't make sense, and so we conclude that it is
437
not less than the other object.
438
439
INPUT:
440
441
- ``right`` - an object
442
443
OUTPUT:
444
445
- boolean - False
446
447
EXAMPLES::
448
449
sage: Color('red') < Color('red')
450
False
451
sage: Color('blue') < Color('red')
452
False
453
sage: Color('red') < "xyzzy"
454
False
455
"""
456
return False
457
458
def __le__(self, right):
459
"""
460
Check whether a :class:`Color` object is less than or equal to
461
some other object. It wouldn't make sense for it to be less than
462
the other object, so we treat this the same as an equality
463
check.
464
465
INPUT:
466
467
- ``right`` - an object
468
469
OUTPUT:
470
471
- boolean - False
472
473
EXAMPLES::
474
475
sage: Color('red') <= Color('red')
476
True
477
sage: Color('blue') <= Color('red')
478
False
479
sage: Color('red') <= "xyzzy"
480
False
481
"""
482
return self == right
483
484
def __eq__(self, right):
485
"""
486
Compare two :class:`Color` objects to determine whether
487
they refer to the same color.
488
489
INPUT:
490
491
- ``right`` - a :class:`Color` instance
492
493
OUTPUT:
494
495
- boolean - True if the two colors are the same, False if different
496
497
EXAMPLES::
498
499
sage: Color('red') == Color((1,0,0))
500
True
501
sage: Color('blue') == Color((0,1,0))
502
False
503
sage: Color('blue') + Color((0,1,0)) == Color((0,0.5,0.5))
504
True
505
sage: Color(0.2,0.3,0.2) == False
506
False
507
"""
508
if isinstance(right, Color):
509
return self.__rgb == right.__rgb
510
else:
511
return False
512
513
def __ne__(self, right):
514
"""
515
Compare two :class:`Color` objects to determine whether
516
they refer to different colors.
517
518
INPUT:
519
520
- ``right`` - a :class:`Color` instance
521
522
OUTPUT:
523
524
- boolean - True if the two colors are different,
525
False if they're the same
526
527
EXAMPLES::
528
529
sage: Color('green') != Color('yellow')
530
True
531
sage: Color('red') != Color(1,0,0)
532
False
533
sage: Color('yellow') != Color(1,1,0)
534
False
535
sage: Color('blue') != 23
536
True
537
"""
538
return not (self == right)
539
540
def __gt__(self, right):
541
"""
542
Check whether a :class:`Color` object is greater than some other
543
object. This doesn't make sense, and so we conclude that it is
544
not greater than the other object.
545
546
INPUT:
547
548
- ``right`` - an object
549
550
OUTPUT:
551
552
- boolean - False
553
554
EXAMPLES::
555
556
sage: Color('red') > Color('red')
557
False
558
sage: Color('blue') > Color('red')
559
False
560
sage: Color('red') > "xyzzy"
561
False
562
"""
563
return False
564
565
def __ge__(self, right):
566
"""
567
Check whether a :class:`Color` object is greater than or equal
568
to some other object. It wouldn't make sense for it to be
569
greater than the other object, so we treat this the same as an
570
equality check.
571
572
INPUT:
573
574
- ``right`` - an object
575
576
OUTPUT:
577
578
- boolean - False
579
580
EXAMPLES::
581
582
sage: Color('red') >= Color('red')
583
True
584
sage: Color('blue') >= Color('red')
585
False
586
sage: Color('red') >= "xyzzy"
587
False
588
"""
589
return self == right
590
591
def __hash__(self):
592
"""
593
Return the hash value of a color.
594
Equal colors return equal hash values.
595
596
OUTPUT:
597
598
- a hash value
599
600
EXAMPLES::
601
602
sage: hash(Color('red')) # random
603
873150856
604
sage: hash(Color('red')) == hash(Color((1,0,0)))
605
True
606
"""
607
return hash(self.__rgb)
608
609
def blend(self, color, fraction=0.5):
610
"""
611
Return a color blended with the given ``color`` by a given
612
``fraction``. The algorithm interpolates linearly between the
613
colors' corresponding R, G, and B coordinates.
614
615
INPUT:
616
617
- ``color`` - a :class:`Color` instance or float-convertible
618
3-tuple/list; the color with which to blend this color
619
620
- ``fraction`` - a float-convertible number; the fraction of
621
``color`` to blend with this color
622
623
OUTPUT:
624
625
- a **new** :class:`Color` instance
626
627
EXAMPLES::
628
629
sage: from sage.plot.colors import red, blue, lime
630
sage: red.blend(blue)
631
RGB color (0.5, 0.0, 0.5)
632
sage: red.blend(blue, fraction=0.0)
633
RGB color (1.0, 0.0, 0.0)
634
sage: red.blend(blue, fraction=1.0)
635
RGB color (0.0, 0.0, 1.0)
636
sage: lime.blend((0.3, 0.5, 0.7))
637
RGB color (0.15, 0.75, 0.35)
638
sage: blue.blend(blue)
639
RGB color (0.0, 0.0, 1.0)
640
sage: red.blend(lime, fraction=0.3)
641
RGB color (0.7, 0.3, 0.0)
642
sage: blue.blend((0.0, 0.9, 0.2), fraction=0.2)
643
RGB color (0.0, 0.18000000000000002, 0.8400000000000001)
644
sage: red.blend(0.2)
645
Traceback (most recent call last):
646
...
647
TypeError: 0.200000000000000 must be a Color or float-convertible 3-tuple/list
648
"""
649
fraction = float(fraction)
650
if isinstance(color, Color):
651
color = color.__rgb
652
if isinstance(color, (list, tuple)) and len(color) == 3:
653
color = map(float, color)
654
return Color(rgbcolor([(1 - fraction) * a + fraction * b
655
for a, b in zip(self.__rgb, color)]))
656
raise TypeError("%s must be a Color or float-convertible 3-tuple/list" % (color, ))
657
658
def __add__(self, right):
659
"""
660
Return a color "added" on the right to another color, with
661
:meth:`blend`.
662
663
INPUT:
664
665
- ``right`` - a :class:`Color` instance or float-convertible
666
3-tuple/list
667
668
OUTPUT:
669
670
- a **new** :class:`Color` instance
671
672
EXAMPLES::
673
674
sage: from sage.plot.colors import red, blue, lime
675
sage: red + blue + lime
676
RGB color (0.25, 0.5, 0.25)
677
sage: from sage.plot.colors import cyan, magenta, yellow
678
sage: cyan + magenta + yellow
679
RGB color (0.75, 0.75, 0.5)
680
sage: c1 = Color(0.1, 0.5, 0.8); c2 = Color(0.2, 0.4, 0.7, space='hsv')
681
sage: c1 + 0.1
682
Traceback (most recent call last):
683
...
684
TypeError: 0.100000000000000 must be a Color or float-convertible 3-tuple/list
685
sage: c2 + [0.5, 0.2, 0.9]
686
RGB color (0.572, 0.44999999999999996, 0.66)
687
sage: c1.__add__(red).__add__((0.9, 0.2, 1/3))
688
RGB color (0.7250000000000001, 0.225, 0.3666666666666667)
689
sage: c1 + c2
690
RGB color (0.37199999999999994, 0.6, 0.61)
691
"""
692
return self.blend(right)
693
694
def __radd__(self, left):
695
"""
696
Return a color "added" on the left to another color, with
697
:meth:`blend`.
698
699
INPUT:
700
701
- ``left`` - a :class:`Color` instance or float-convertible
702
3-tuple/list
703
704
OUTPUT:
705
706
- a **new** :class:`Color` instance
707
708
EXAMPLES::
709
710
sage: from sage.plot.colors import olive, orchid
711
sage: olive + orchid
712
RGB color (0.6784313725490196, 0.47058823529411764, 0.4196078431372549)
713
sage: d1 = Color(0.1, 0.5, 0.8, space='hls'); d2 = Color(0.2, 0.4, 0.7)
714
sage: [0.5, 0.2, 0.9] + d2
715
RGB color (0.35, 0.30000000000000004, 0.8)
716
sage: 0.1 + d1
717
Traceback (most recent call last):
718
...
719
TypeError: 0.100000000000000 must be a Color or float-convertible 3-tuple/list
720
sage: d2.__radd__(Color('brown')).__radd__((0.9, 0.2, 1/3))
721
RGB color (0.661764705882353, 0.2411764705882353, 0.38284313725490193)
722
"""
723
return self + left
724
725
def __mul__(self, right):
726
"""
727
Return a color whose RGB coordinates are this color's
728
coordinates multiplied on the right by a scalar.
729
730
INPUT:
731
732
- ``right`` - a float-convertible number
733
734
OUTPUT:
735
736
- a **new** :class:`Color` instance
737
738
EXAMPLES::
739
740
sage: Color('yellow') * 0.5
741
RGB color (0.5, 0.5, 0.0)
742
sage: Color('yellow') * (9.0 / 8.0) # reduced modulo 1.0
743
RGB color (0.125, 0.125, 0.0)
744
sage: from sage.plot.colors import cyan, grey, indianred
745
sage: cyan * 0.3 + grey * 0.1 + indianred * 0.6
746
RGB color (0.25372549019607843, 0.1957843137254902, 0.1957843137254902)
747
sage: indianred.__mul__(42)
748
RGB color (0.764705882352942, 0.1529411764705877, 0.1529411764705877)
749
"""
750
right = float(right)
751
return Color([x * right for x in self.__rgb])
752
753
def __rmul__(self, left):
754
"""
755
Return a color whose RGB coordinates are this color's
756
coordinates multiplied on the left by a scalar.
757
758
INPUT:
759
760
- ``left`` - a float-convertible number
761
762
OUTPUT:
763
764
- a **new** :class:`Color` instance
765
766
EXAMPLES::
767
768
sage: from sage.plot.colors import aqua, cornsilk, tomato
769
sage: 0.3 * aqua
770
RGB color (0.0, 0.3, 0.3)
771
sage: Color('indianred').__rmul__(42)
772
RGB color (0.764705882352942, 0.1529411764705877, 0.1529411764705877)
773
"""
774
return self * left
775
776
def __div__(self, right):
777
"""
778
Return a color whose RGB coordinates are this color's
779
coordinates divided by a scalar. This method is called for
780
"classic division."
781
782
INPUT:
783
784
- ``right`` - a float-convertible, non-zero number
785
786
OUTPUT:
787
788
- a **new** instance of :class:`Color`
789
790
EXAMPLES::
791
792
sage: from sage.plot.colors import papayawhip, yellow
793
sage: yellow / 4
794
RGB color (0.25, 0.25, 0.0)
795
sage: yellow.__div__(4)
796
RGB color (0.25, 0.25, 0.0)
797
sage: (papayawhip + Color(0.5, 0.5, 0.1) + yellow) / 3.0
798
RGB color (0.29166666666666663, 0.286437908496732, 0.07794117647058824)
799
sage: vector((papayawhip / 2).rgb()) == vector((papayawhip * 0.5).rgb())
800
True
801
sage: yellow.__div__(1/4)
802
RGB color (0.0, 0.0, 0.0)
803
sage: Color('black') / 0.0
804
Traceback (most recent call last):
805
...
806
ZeroDivisionError: float division by zero
807
sage: papayawhip / yellow
808
Traceback (most recent call last):
809
...
810
TypeError: float() argument must be a string or a number
811
"""
812
return self * (float(1.0) / float(right))
813
814
def __truediv__(self, right):
815
"""
816
Return a color whose RGB coordinates are this color's
817
coordinates divided by a scalar. This method is called for
818
"true division."
819
820
INPUT:
821
822
- ``right`` - a float-convertible, non-zero number
823
824
OUTPUT:
825
826
- a **new** instance of :class:`Color`
827
828
EXAMPLES::
829
830
sage: from __future__ import division
831
sage: from sage.plot.colors import yellow, gold
832
sage: yellow / 4
833
RGB color (0.25, 0.25, 0.0)
834
sage: yellow.__truediv__(4)
835
RGB color (0.25, 0.25, 0.0)
836
sage: gold / pi + yellow * e
837
RGB color (0.51829585732141..., 0.49333037605210..., 0.0)
838
"""
839
return self.__div__(right)
840
841
def __hex__(self):
842
"""
843
Return a hexadecimal string representation of this color.
844
This is just the color's HTML hex representation without the
845
leading '#'.
846
847
OUTPUT:
848
849
- a string of length 6
850
851
EXAMPLES::
852
853
sage: from sage.plot.colors import whitesmoke
854
sage: hex(whitesmoke)
855
'f5f5f5'
856
sage: whitesmoke.html_color() == '#' + hex(whitesmoke)
857
True
858
sage: hex(Color(0.5, 1.0, 1.0, space='hsv'))
859
'00ffff'
860
sage: set([len(hex(Color(t, 1-t, t * t))) for t in srange(0, 1, 0.1)])
861
set([6])
862
"""
863
return self.html_color()[1:]
864
865
def __iter__(self):
866
"""
867
Return an iterator over the RGB coordinates of this color.
868
869
OUTPUT:
870
871
- a tupleiterator
872
873
EXAMPLES::
874
875
sage: from sage.plot.colors import dodgerblue, maroon
876
sage: r, g, b = dodgerblue
877
sage: r
878
0.11764705882352941
879
sage: g
880
0.5647058823529412
881
sage: b
882
1.0
883
sage: vector(maroon) == vector(Color(maroon)) == vector(Color('maroon'))
884
True
885
"""
886
return iter(self.__rgb)
887
888
def __getitem__(self, i):
889
"""
890
Return the Red (0th), Green (1st), or Blue (2nd) coordinate of this
891
color via index access.
892
893
INPUT:
894
895
- ``i`` - an integer; the 0-based coordinate to retrieve
896
897
OUTPUT:
898
899
- a float
900
901
EXAMPLES::
902
903
sage: from sage.plot.colors import crimson, midnightblue
904
sage: Color('#badfad')[0]
905
0.7294117647058823
906
sage: (crimson[0], crimson[1], crimson[2]) == crimson.rgb()
907
True
908
sage: midnightblue[2] == midnightblue[-1]
909
True
910
sage: midnightblue[3]
911
Traceback (most recent call last):
912
...
913
IndexError: tuple index out of range
914
"""
915
return self.__rgb[i]
916
917
def rgb(self):
918
"""
919
Return the underlying Red-Green-Blue (RGB) coordinates of this
920
color.
921
922
OUTPUT:
923
924
- a 3-tuple of floats
925
926
EXAMPLES::
927
928
sage: Color(0.3, 0.5, 0.7).rgb()
929
(0.3, 0.5, 0.7)
930
sage: Color('#8000ff').rgb()
931
(0.5019607843137255, 0.0, 1.0)
932
sage: from sage.plot.colors import orange
933
sage: orange.rgb()
934
(1.0, 0.6470588235294118, 0.0)
935
sage: Color('magenta').rgb()
936
(1.0, 0.0, 1.0)
937
sage: Color(1, 0.7, 0.9, space='hsv').rgb()
938
(0.9, 0.2700000000000001, 0.2700000000000001)
939
"""
940
return self.__rgb
941
942
def hls(self):
943
"""
944
Return the Hue-Lightness-Saturation (HLS) coordinates of this
945
color.
946
947
OUTPUT:
948
949
- a 3-tuple of floats
950
951
EXAMPLES::
952
953
sage: Color(0.3, 0.5, 0.7, space='hls').hls()
954
(0.30000000000000004, 0.5, 0.7)
955
sage: Color(0.3, 0.5, 0.7, space='hsl').hls()
956
(0.30000000000000004, 0.7, 0.5000000000000001)
957
sage: Color('#aabbcc').hls()
958
(0.5833333333333334, 0.7333333333333334, 0.25000000000000017)
959
sage: from sage.plot.colors import orchid
960
sage: orchid.hls()
961
(0.8396226415094339, 0.6470588235294117, 0.5888888888888889)
962
"""
963
return tuple(map(float, rgb_to_hls(*self.__rgb)))
964
965
def hsl(self):
966
"""
967
Return the Hue-Saturation-Lightness (HSL) coordinates of this
968
color.
969
970
OUTPUT:
971
972
- a 3-tuple of floats
973
974
EXAMPLES::
975
976
sage: Color(1,0,0).hsl()
977
(0.0, 1.0, 0.5)
978
sage: from sage.plot.colors import orchid
979
sage: orchid.hsl()
980
(0.8396226415094339, 0.5888888888888889, 0.6470588235294117)
981
sage: Color('#aabbcc').hsl()
982
(0.5833333333333334, 0.25000000000000017, 0.7333333333333334)
983
"""
984
h, l, s = tuple(map(float, rgb_to_hls(*self.__rgb)))
985
return (h, s, l)
986
987
def hsv(self):
988
"""
989
Return the Hue-Saturation-Value (HSV) coordinates of this
990
color.
991
992
OUTPUT:
993
994
- a 3-tuple of floats
995
996
EXAMPLES::
997
998
sage: from sage.plot.colors import red
999
sage: red.hsv()
1000
(0.0, 1.0, 1.0)
1001
sage: Color(1,1,1).hsv()
1002
(0.0, 0.0, 1.0)
1003
sage: Color('gray').hsv()
1004
(0.0, 0.0, 0.5019607843137255)
1005
"""
1006
return tuple(map(float, rgb_to_hsv(*self.__rgb)))
1007
1008
def html_color(self):
1009
"""
1010
Return a HTML hex representation for this color.
1011
1012
OUTPUT:
1013
1014
- a string of length 7.
1015
1016
EXAMPLES::
1017
1018
sage: Color('yellow').html_color()
1019
'#ffff00'
1020
sage: Color('#fedcba').html_color()
1021
'#fedcba'
1022
sage: Color(0.0, 1.0, 0.0).html_color()
1023
'#00ff00'
1024
sage: from sage.plot.colors import honeydew
1025
sage: honeydew.html_color()
1026
'#f0fff0'
1027
"""
1028
return float_to_html(*self.__rgb)
1029
1030
def lighter(self, fraction=1.0/3.0):
1031
"""
1032
Return a lighter "shade" of this RGB color by
1033
:meth:`blend`-ing it with white. This is **not** an inverse
1034
of :meth:`darker`.
1035
1036
INPUT:
1037
1038
- ``fraction`` - a float (default: 1.0/3.0); blending fraction
1039
to apply
1040
1041
OUTPUT:
1042
1043
- a **new** instance of :class:`Color`
1044
1045
EXAMPLES::
1046
1047
sage: from sage.plot.colors import khaki
1048
sage: khaki.lighter()
1049
RGB color (0.9607843137254903, 0.934640522875817, 0.6993464052287582)
1050
sage: Color('white').lighter().darker()
1051
RGB color (0.6666666666666667, 0.6666666666666667, 0.6666666666666667)
1052
sage: Color('#abcdef').lighter(1/4)
1053
RGB color (0.7529411764705882, 0.8529411764705883, 0.9529411764705882)
1054
sage: Color(1, 0, 8/9, space='hsv').lighter()
1055
RGB color (0.925925925925926, 0.925925925925926, 0.925925925925926)
1056
"""
1057
return self.blend((1.0, 1.0, 1.0), fraction)
1058
1059
def darker(self, fraction=1.0/3.0):
1060
"""
1061
Return a darker "shade" of this RGB color by :meth:`blend`-ing
1062
it with black. This is **not** an inverse of :meth:`lighter`.
1063
1064
INPUT:
1065
1066
- ``fraction`` - a float (default: 1.0/3.0); blending fraction
1067
to apply
1068
1069
OUTPUT:
1070
1071
- a new instance of :class:`Color`
1072
1073
EXAMPLES::
1074
1075
sage: from sage.plot.colors import black
1076
sage: vector(black.darker().rgb()) == vector(black.rgb())
1077
True
1078
sage: Color(0.4, 0.6, 0.8).darker(0.1)
1079
RGB color (0.36000000000000004, 0.54, 0.7200000000000001)
1080
sage: Color(.1,.2,.3,space='hsl').darker()
1081
RGB color (0.24000000000000002, 0.20800000000000002, 0.16)
1082
"""
1083
return self.blend((0.0, 0.0, 0.0), fraction)
1084
1085
1086
class ColorsDict(dict):
1087
"""
1088
A dict-like collection of colors, accessible via key or attribute.
1089
For a list of color names, evaluate::
1090
1091
sage: sorted(colors)
1092
['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', ...]
1093
"""
1094
def __init__(self):
1095
"""
1096
Constructs a dict-like collection of colors. The keys are the
1097
color names (i.e., strings) and the values are RGB 3-tuples of
1098
floats.
1099
1100
EXAMPLES::
1101
1102
sage: from sage.plot.colors import ColorsDict
1103
sage: cols = ColorsDict()
1104
sage: set([(type(c), type(cols[c])) for c in cols])
1105
set([(<type 'str'>, <class 'sage.plot.colors.Color'>)])
1106
sage: sorted(cols)
1107
['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', ...]
1108
sage: len(cols)
1109
148
1110
"""
1111
# Convert the colors_dict defined above to Color instances.
1112
for k in colors_dict:
1113
self[k] = Color(colors_dict[k])
1114
1115
def __getattr__(self, name):
1116
"""
1117
Gets a color via attribute access.
1118
1119
INPUT:
1120
1121
- ``name`` - a string; the name of the color to return
1122
1123
OUTPUT:
1124
1125
- a RGB 3-tuple of floats
1126
1127
EXAMPLES::
1128
1129
sage: from sage.plot.colors import ColorsDict, blue
1130
sage: cols = ColorsDict()
1131
sage: cols.blue
1132
RGB color (0.0, 0.0, 1.0)
1133
sage: cols['blue']
1134
RGB color (0.0, 0.0, 1.0)
1135
sage: blue
1136
RGB color (0.0, 0.0, 1.0)
1137
sage: cols.punk
1138
Traceback (most recent call last):
1139
...
1140
AttributeError: 'ColorsDict' has no attribute or colormap punk
1141
"""
1142
try:
1143
return self.__getitem__(name)
1144
except KeyError:
1145
raise AttributeError("'%s' has no attribute or colormap %s"%(type(self).__name__,name))
1146
1147
def __dir__(self):
1148
"""
1149
Returns an approximate list of attribute names, including the
1150
color names.
1151
1152
OUTPUT:
1153
1154
- a list of strings
1155
1156
EXAMPLES::
1157
1158
sage: from sage.plot.colors import ColorsDict
1159
sage: cols = ColorsDict()
1160
sage: 'green' in dir(cols)
1161
True
1162
"""
1163
methods = ['__dir__', '__getattr__']
1164
return dir(super(ColorsDict, self)) + methods + self.keys()
1165
1166
colors = ColorsDict()
1167
1168
# Add convenient module-scope colors. These are Color instances.
1169
for c in colors:
1170
vars()[c] = colors[c]
1171
1172
1173
def hue(h, s=1, v=1):
1174
r"""
1175
Convert a Hue-Saturation-Value (HSV) color tuple to a valid
1176
Red-Green-Blue (RGB) tuple. All three inputs should lie in the
1177
interval [0.0, 1.0]; otherwise, they are reduced modulo 1 (see
1178
:func:`mod_one`). In particular ``h=0`` and ``h=1`` yield red,
1179
with the intermediate hues orange, yellow, green, cyan, blue, and
1180
violet as ``h`` increases.
1181
1182
This function makes it easy to sample a broad range of colors for
1183
graphics::
1184
1185
sage: p = Graphics()
1186
sage: for phi in xsrange(0, 2 * pi, 1 / pi):
1187
... p += plot(sin(x + phi), (x, -7, 7), rgbcolor = hue(phi))
1188
sage: p
1189
1190
INPUT:
1191
1192
- ``h`` - a number; the color's hue
1193
1194
- ``s`` - a number (default: 1); the color's saturation
1195
1196
- ``v`` - a number (default: 1); the color's value
1197
1198
OUTPUT:
1199
1200
- a RGB 3-tuple of floats in the interval [0.0, 1.0]
1201
1202
EXAMPLES::
1203
1204
sage: hue(0.6)
1205
(0.0, 0.40000000000000036, 1.0)
1206
sage: from sage.plot.colors import royalblue
1207
sage: royalblue
1208
RGB color (0.2549019607843137, 0.4117647058823529, 0.8823529411764706)
1209
sage: hue(*royalblue.hsv())
1210
(0.2549019607843137, 0.4117647058823529, 0.8823529411764706)
1211
sage: hue(.5, .5, .5)
1212
(0.25, 0.5, 0.5)
1213
1214
.. note :: The HSV to RGB coordinate transformation itself is
1215
given in the source code for the Python library's
1216
:mod:`colorsys` module::
1217
1218
sage: from colorsys import hsv_to_rgb # not tested
1219
sage: hsv_to_rgb?? # not tested
1220
"""
1221
return tuple(map(float, hsv_to_rgb(mod_one(h), mod_one(s), mod_one(v))))
1222
1223
1224
def float_to_html(r, g, b):
1225
"""
1226
Converts a Red-Green-Blue (RGB) color tuple to a HTML hex color.
1227
Each input value should be in the interval [0.0, 1.0]; otherwise,
1228
the values are first reduced modulo one (see :func:`mod_one`).
1229
1230
INPUT:
1231
1232
- ``r`` - a number; the RGB color's "red" intensity
1233
1234
- ``g`` - a number; the RGB color's "green" intensity
1235
1236
- ``b`` - a number; the RGB color's "blue" intensity
1237
1238
OUTPUT:
1239
1240
- a string of length 7, starting with '#'
1241
1242
EXAMPLES::
1243
1244
sage: from sage.plot.colors import float_to_html
1245
sage: float_to_html(1.,1.,0.)
1246
'#ffff00'
1247
sage: float_to_html(.03,.06,.02)
1248
'#070f05'
1249
sage: float_to_html(*Color('brown').rgb())
1250
'#a52a2a'
1251
sage: float_to_html((0.2, 0.6, 0.8))
1252
Traceback (most recent call last):
1253
...
1254
TypeError: float_to_html() takes exactly 3 arguments (1 given)
1255
"""
1256
# TODO: figure out why this is necessary
1257
from sage.rings.integer import Integer
1258
from math import floor
1259
1260
r, g, b = map(mod_one, (r, g, b))
1261
rr = Integer(int(floor(r * 255))).str(base = 16)
1262
gg = Integer(int(floor(g * 255))).str(base = 16)
1263
bb = Integer(int(floor(b * 255))).str(base = 16)
1264
1265
rr = '0' * (2 - len(rr)) + rr
1266
gg = '0' * (2 - len(gg)) + gg
1267
bb = '0' * (2 - len(bb)) + bb
1268
1269
return '#' + rr + gg + bb
1270
1271
1272
def rainbow(n, format='hex'):
1273
"""
1274
Returns a list of colors sampled at equal intervals over the
1275
spectrum, from Hue-Saturation-Value (HSV) coordinates (0, 1, 1) to
1276
(1, 1, 1). This range is red at the extremes, but it covers
1277
orange, yellow, green, cyan, blue, violet, and many other hues in
1278
between. This function is particularly useful for representing
1279
vertex partitions on graphs.
1280
1281
INPUT:
1282
1283
- ``n`` - a number; the length of the list
1284
1285
- ``format`` - a string (default: 'hex'); the output format for
1286
each color in the list; the other choice is 'rgbtuple'
1287
1288
OUTPUT:
1289
1290
- a list of strings or RGB 3-tuples of floats in the interval
1291
[0.0, 1.0]
1292
1293
EXAMPLES::
1294
1295
sage: from sage.plot.colors import rainbow
1296
sage: rainbow(7)
1297
['#ff0000', '#ffda00', '#48ff00', '#00ff91', '#0091ff', '#4800ff', '#ff00da']
1298
sage: rainbow(7, 'rgbtuple')
1299
[(1.0, 0.0, 0.0), (1.0, 0.8571428571428571, 0.0), (0.2857142857142858, 1.0, 0.0), (0.0, 1.0, 0.5714285714285712), (0.0, 0.5714285714285716, 1.0), (0.2857142857142847, 0.0, 1.0), (1.0, 0.0, 0.8571428571428577)]
1300
1301
AUTHORS:
1302
1303
- Robert L. Miller
1304
1305
- Karl-Dieter Crisman (directly use :func:`hsv_to_rgb` for hues)
1306
"""
1307
from sage.rings.integer import Integer
1308
n = Integer(n) # In case n is a Python int and i/n below would give 0!
1309
R = []
1310
1311
for i in range(n):
1312
R.append(tuple(map(float, hsv_to_rgb(i / n, 1, 1))))
1313
1314
if format == 'rgbtuple':
1315
return R
1316
elif format == 'hex':
1317
for j in range(len(R)):
1318
R[j] = float_to_html(*R[j])
1319
return R
1320
1321
1322
# If you change what this accepts, remember to change the documentation
1323
# about cmap where it is used and to test these classes.
1324
def get_cmap(cmap):
1325
r"""
1326
Returns a color map (actually, a matplotlib :class:`Colormap`
1327
object), given its name or a [mixed] list/tuple of RGB list/tuples
1328
and color names. For a list of map names, evaluate::
1329
1330
sage: sorted(colormaps)
1331
['Accent', 'Accent_r', 'Blues', 'Blues_r', ...]
1332
1333
See :func:`rgbcolor` for valid list/tuple element formats.
1334
1335
INPUT:
1336
1337
- ``cmap`` - a string, list, tuple, or
1338
:class:`matplotlib.colors.Colormap`; a string must be a valid
1339
color map name
1340
1341
OUTPUT:
1342
1343
- a :class:`matplotlib.colors.Colormap` instance
1344
1345
EXAMPLES::
1346
1347
sage: from sage.plot.colors import get_cmap
1348
sage: get_cmap('jet')
1349
<matplotlib.colors.LinearSegmentedColormap instance at 0x...>
1350
sage: get_cmap([(0,0,0), (0.5,0.5,0.5), (1,1,1)])
1351
<matplotlib.colors.ListedColormap instance at 0x...>
1352
sage: get_cmap(['green', 'lightblue', 'blue'])
1353
<matplotlib.colors.ListedColormap instance at 0x...>
1354
sage: get_cmap(((0.5, 0.3, 0.2), [1.0, 0.0, 0.5], 'purple', Color(0.5,0.5,1, space='hsv')))
1355
<matplotlib.colors.ListedColormap instance at 0x...>
1356
sage: get_cmap('jolies')
1357
Traceback (most recent call last):
1358
...
1359
RuntimeError: Color map jolies not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)
1360
sage: get_cmap('mpl')
1361
Traceback (most recent call last):
1362
...
1363
RuntimeError: Color map mpl not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)
1364
"""
1365
# matplotlib color maps
1366
global cm
1367
if not cm:
1368
from matplotlib import cm
1369
from matplotlib.colors import ListedColormap, Colormap
1370
1371
if isinstance(cmap, Colormap):
1372
return cmap
1373
1374
elif isinstance(cmap, str):
1375
if not cmap in cm.datad.keys():
1376
raise RuntimeError("Color map %s not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)" % cmap)
1377
return cm.__dict__[cmap]
1378
1379
elif isinstance(cmap, (list, tuple)):
1380
cmap = map(rgbcolor, cmap)
1381
return ListedColormap(cmap)
1382
1383
1384
class Colormaps(collections.MutableMapping):
1385
"""
1386
A dict-like collection of lazily-loaded matplotlib color maps.
1387
For a list of map names, evaluate::
1388
1389
sage: sorted(colormaps)
1390
['Accent', 'Accent_r', 'Blues', 'Blues_r', ...]
1391
"""
1392
def __init__(self):
1393
"""
1394
Constructs an empty mutable collection of color maps.
1395
1396
EXAMPLES::
1397
1398
sage: from sage.plot.colors import Colormaps
1399
sage: maps = Colormaps()
1400
sage: len(maps.maps)
1401
0
1402
"""
1403
self.maps = {}
1404
1405
def load_maps(self):
1406
"""
1407
If it's necessary, loads matplotlib's color maps and adds them
1408
to the collection.
1409
1410
EXAMPLES::
1411
1412
sage: from sage.plot.colors import Colormaps
1413
sage: maps = Colormaps()
1414
sage: len(maps.maps)
1415
0
1416
sage: maps.load_maps()
1417
sage: len(maps.maps)>130
1418
True
1419
"""
1420
global cm
1421
if not cm:
1422
from matplotlib import cm
1423
if not self.maps:
1424
for cmap in cm.datad.keys():
1425
self.maps[cmap] = cm.__getattribute__(cmap)
1426
1427
def __dir__(self):
1428
"""
1429
Returns an approximate list of attribute names, including the
1430
color map names.
1431
1432
OUTPUT:
1433
1434
- a list of strings
1435
1436
EXAMPLES::
1437
1438
sage: from sage.plot.colors import Colormaps
1439
sage: maps = Colormaps()
1440
sage: 'Accent' in dir(maps)
1441
True
1442
"""
1443
self.load_maps()
1444
methods = ['load_maps', '__dir__', '__len__', '__iter__',
1445
'__contains__', '__getitem__', '__getattr__',
1446
'__setitem__', '__delitem__']
1447
return dir(super(Colormaps, self)) + methods + self.keys()
1448
1449
def __len__(self):
1450
"""
1451
Returns the number of color maps.
1452
1453
OUTPUT:
1454
1455
- an int
1456
1457
EXAMPLES::
1458
1459
sage: from sage.plot.colors import Colormaps
1460
sage: maps = Colormaps()
1461
sage: len(maps)>130
1462
True
1463
"""
1464
self.load_maps()
1465
return len(self.maps)
1466
1467
def __iter__(self):
1468
"""
1469
Returns an iterator over the color map collection.
1470
1471
OUTPUT:
1472
1473
- a dictionary key iterator instance
1474
1475
EXAMPLES::
1476
1477
sage: from sage.plot.colors import Colormaps
1478
sage: maps = Colormaps()
1479
sage: count = 0
1480
sage: for m in maps: count += 1
1481
sage: count == len(maps)
1482
True
1483
"""
1484
self.load_maps()
1485
return iter(self.maps)
1486
1487
def __contains__(self, name):
1488
"""
1489
Returns whether a map is in the color maps collection.
1490
1491
INPUT:
1492
1493
- ``name`` - a string; the name of the map to query
1494
1495
OUTPUT:
1496
1497
- a boolean
1498
1499
EXAMPLES::
1500
1501
sage: from sage.plot.colors import Colormaps
1502
sage: maps = Colormaps()
1503
sage: 'summer' in maps
1504
True
1505
sage: 'not really a color map' in maps
1506
False
1507
"""
1508
self.load_maps()
1509
return name in self.maps
1510
1511
def __getitem__(self, name):
1512
"""
1513
Gets a color map from the collection via key access.
1514
1515
INPUT:
1516
1517
- ``name`` - a string; the name of the map return
1518
1519
OUTPUT:
1520
1521
- an instance of :class:`matplotlib.colors.Colormap`
1522
1523
EXAMPLES::
1524
1525
sage: from sage.plot.colors import Colormaps
1526
sage: maps = Colormaps()
1527
sage: maps.get('Oranges')
1528
<matplotlib.colors.LinearSegmentedColormap instance at ...>
1529
sage: maps['copper']
1530
<matplotlib.colors.LinearSegmentedColormap instance at ...>
1531
sage: maps.get('not a color map')
1532
sage: maps['not a color map']
1533
Traceback (most recent call last):
1534
...
1535
KeyError: "no colormap with name 'not a color map'"
1536
"""
1537
self.load_maps()
1538
try:
1539
return self.maps[name]
1540
except KeyError:
1541
raise KeyError("no colormap with name '%s'" % name)
1542
1543
def __getattr__(self, name):
1544
"""
1545
Gets a color map from the collection via attribute access.
1546
1547
INPUT:
1548
1549
- ``name`` - a string; the name of the map to return
1550
1551
OUTPUT:
1552
1553
- an instance of :class:`matplotlib.colors.Colormap`
1554
1555
EXAMPLES::
1556
1557
sage: from sage.plot.colors import Colormaps
1558
sage: maps = Colormaps()
1559
sage: maps.pink
1560
<matplotlib.colors.LinearSegmentedColormap instance at ...>
1561
sage: maps.punk
1562
Traceback (most recent call last):
1563
...
1564
AttributeError: 'Colormaps' has no attribute or colormap punk
1565
sage: maps['punk']
1566
Traceback (most recent call last):
1567
...
1568
KeyError: "no colormap with name 'punk'"
1569
sage: maps['bone'] == maps.bone
1570
True
1571
"""
1572
try:
1573
return self.__getitem__(name)
1574
except KeyError:
1575
raise AttributeError("'%s' has no attribute or colormap %s"%(type(self).__name__,name))
1576
1577
def __repr__(self):
1578
"""
1579
Returns a string representation of the color map collection.
1580
1581
OUTPUT:
1582
1583
- a string
1584
1585
EXAMPLES::
1586
1587
sage: from sage.plot.colors import Colormaps
1588
sage: maps = Colormaps()
1589
sage: maps
1590
{...}
1591
sage: type(repr(maps))
1592
<type 'str'>
1593
"""
1594
self.load_maps()
1595
return repr(self.maps)
1596
1597
def __setitem__(self, name, colormap):
1598
"""
1599
Adds a color map to the collection.
1600
1601
INPUT:
1602
1603
- ``name`` - a string; the name of the map to add
1604
1605
- ``colormap`` - an instance of
1606
:class:`matplotlib.colors.Colormap`; the color map to add
1607
1608
EXAMPLES::
1609
1610
sage: from sage.plot.colors import Colormaps, get_cmap
1611
sage: maps = Colormaps()
1612
sage: count = len(maps)
1613
sage: my_map = get_cmap(['chartreuse', '#007', (1.0, 0.0, 0.0)])
1614
sage: maps['my_map'] = my_map
1615
sage: 'my_map' in maps
1616
True
1617
sage: count + 1 == len(maps)
1618
True
1619
"""
1620
self.load_maps()
1621
self.maps[name] = colormap
1622
1623
def __delitem__(self, name):
1624
"""
1625
Removes a color map from the collection.
1626
1627
INPUT:
1628
1629
- ``name`` - a string; the name of the map to remove
1630
1631
EXAMPLES::
1632
1633
sage: from sage.plot.colors import Colormaps
1634
sage: maps = Colormaps()
1635
sage: count = len(maps)
1636
sage: maps.popitem()
1637
('Spectral', <matplotlib.colors.LinearSegmentedColormap instance at ...>)
1638
sage: count - 1 == len(maps)
1639
True
1640
"""
1641
self.load_maps()
1642
del self.maps[name]
1643
1644
colormaps = Colormaps()
1645
1646