Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/plot/colors.py
8815 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(u'purple')
297
(0.5019607843137255, 0.0, 0.5019607843137255)
298
sage: rgbcolor('#fa0')
299
(1.0, 0.6666666666666666, 0.0)
300
sage: rgbcolor(u'#fa0')
301
(1.0, 0.6666666666666666, 0.0)
302
sage: rgbcolor('#ffffff')
303
(1.0, 1.0, 1.0)
304
sage: rgbcolor(u'#ffffff')
305
(1.0, 1.0, 1.0)
306
sage: rgbcolor((1,1/2,1/3))
307
(1.0, 0.5, 0.3333333333333333)
308
sage: rgbcolor([1,1/2,1/3])
309
(1.0, 0.5, 0.3333333333333333)
310
sage: rgbcolor((1,1,1), space='hsv')
311
(1.0, 0.0, 0.0)
312
sage: rgbcolor((0.5,0.75,1), space='hls')
313
(0.5, 0.9999999999999999, 1.0)
314
sage: rgbcolor((0.5,1.0,0.75), space='hsl')
315
(0.5, 0.9999999999999999, 1.0)
316
sage: rgbcolor([1,2,255]) # WARNING -- numbers are reduced mod 1!!
317
(1.0, 0.0, 0.0)
318
sage: rgbcolor('#abcd')
319
Traceback (most recent call last):
320
...
321
ValueError: color hex string (= 'abcd') must have length 3 or 6
322
sage: rgbcolor('fff')
323
Traceback (most recent call last):
324
...
325
ValueError: unknown color 'fff'
326
sage: rgbcolor(1)
327
Traceback (most recent call last):
328
...
329
TypeError: '1' must be a Color, list, tuple, or string
330
sage: rgbcolor((0.2,0.8,1), space='grassmann')
331
Traceback (most recent call last):
332
...
333
ValueError: space must be one of 'rgb', 'hsv', 'hsl', 'hls'
334
sage: rgbcolor([0.4, 0.1])
335
Traceback (most recent call last):
336
...
337
ValueError: color list or tuple '[0.400000000000000, 0.100000000000000]' must have 3 entries, one for each RGB, HSV, HLS, or HSL channel
338
"""
339
if isinstance(c, Color):
340
return c.rgb()
341
342
if isinstance(c, basestring):
343
if len(c) > 0 and c[0] == '#':
344
# Assume an HTML-like color, e.g., #00ffff or #ab0.
345
return html_to_float(c)
346
else:
347
try:
348
return colors[c].rgb()
349
except KeyError:
350
raise ValueError("unknown color '%s'" % c)
351
352
elif isinstance(c, (list, tuple)):
353
if len(c) != 3:
354
raise ValueError("color list or tuple '%s' must have 3 entries, one for each RGB, HSV, HLS, or HSL channel" % (c, ))
355
c = map(mod_one, list(c))
356
if space == 'rgb':
357
return tuple(c)
358
elif space == 'hsv':
359
return tuple(map(float, hsv_to_rgb(*c)))
360
elif space == 'hls':
361
return tuple(map(float, hls_to_rgb(*c)))
362
elif space == 'hsl':
363
return tuple(map(float, hls_to_rgb(c[0], c[2], c[1])))
364
else:
365
raise ValueError("space must be one of 'rgb', 'hsv', 'hsl', 'hls'")
366
367
raise TypeError("'%s' must be a Color, list, tuple, or string" % c)
368
369
370
# For backward compatibility.
371
to_mpl_color = rgbcolor
372
373
374
class Color(object):
375
def __init__(self, r='#0000ff', g=None, b=None, space='rgb'):
376
"""
377
An Red-Green-Blue (RGB) color model color object. For most
378
consumer-grade devices (e.g., CRTs, LCDs, and printers), as
379
well as internet applications, this is a point in the sRGB
380
absolute color space. The Hue-Saturation-Lightness (HSL),
381
Hue-Lightness-Saturation (HLS), and Hue-Saturation-Value (HSV)
382
spaces are useful alternate representations, or coordinate
383
transformations, of this space. Coordinates in all of these
384
spaces are floating point values in the interval [0.0, 1.0].
385
386
.. note:: All instantiations of :class:`Color` are converted
387
to an internal RGB floating point 3-tuple. This is
388
likely to degrade precision.
389
390
INPUT:
391
392
- ``r,g,b`` - either a triple of floats between 0 and 1,
393
OR ``r`` - a color name string or HTML color hex string
394
395
- ``space`` - a string (default: 'rgb'); the coordinate system
396
(other choices are 'hsl', 'hls', and 'hsv') in which to
397
interpret a triple of floats
398
399
EXAMPLES::
400
401
sage: Color('purple')
402
RGB color (0.5019607843137255, 0.0, 0.5019607843137255)
403
sage: Color('#8000ff')
404
RGB color (0.5019607843137255, 0.0, 1.0)
405
sage: Color(0.5,0,1)
406
RGB color (0.5, 0.0, 1.0)
407
sage: Color(0.5, 1.0, 1, space='hsv')
408
RGB color (0.0, 1.0, 1.0)
409
sage: Color(0.25, 0.5, 0.5, space='hls')
410
RGB color (0.5000000000000001, 0.75, 0.25)
411
sage: Color(1, 0, 1/3, space='hsl')
412
RGB color (0.3333333333333333, 0.3333333333333333, 0.3333333333333333)
413
sage: from sage.plot.colors import chocolate
414
sage: Color(chocolate)
415
RGB color (0.8235294117647058, 0.4117647058823529, 0.11764705882352941)
416
"""
417
if g is None and b is None:
418
self._rgb = rgbcolor(r)
419
else:
420
self._rgb = rgbcolor((r, g, b), space=space)
421
422
def __repr__(self):
423
"""
424
Return a string representation of this color.
425
426
OUTPUT:
427
428
- a string
429
430
EXAMPLES::
431
432
sage: Color('#8000ff').__repr__()
433
'RGB color (0.5019607843137255, 0.0, 1.0)'
434
sage: Color(1, 0.5, 1/16, space='hsl').__repr__()
435
'RGB color (0.09375, 0.03125, 0.03125)'
436
"""
437
return "RGB color %s" % (self._rgb, )
438
439
def __lt__(self, right):
440
"""
441
Check whether a :class:`Color` object is less than some other
442
object. This doesn't make sense, and so we conclude that it is
443
not less than the other object.
444
445
INPUT:
446
447
- ``right`` - an object
448
449
OUTPUT:
450
451
- boolean - False
452
453
EXAMPLES::
454
455
sage: Color('red') < Color('red')
456
False
457
sage: Color('blue') < Color('red')
458
False
459
sage: Color('red') < "xyzzy"
460
False
461
"""
462
return False
463
464
def __le__(self, right):
465
"""
466
Check whether a :class:`Color` object is less than or equal to
467
some other object. It wouldn't make sense for it to be less than
468
the other object, so we treat this the same as an equality
469
check.
470
471
INPUT:
472
473
- ``right`` - an object
474
475
OUTPUT:
476
477
- boolean - False
478
479
EXAMPLES::
480
481
sage: Color('red') <= Color('red')
482
True
483
sage: Color('blue') <= Color('red')
484
False
485
sage: Color('red') <= "xyzzy"
486
False
487
"""
488
return self == right
489
490
def __eq__(self, right):
491
"""
492
Compare two :class:`Color` objects to determine whether
493
they refer to the same color.
494
495
INPUT:
496
497
- ``right`` - a :class:`Color` instance
498
499
OUTPUT:
500
501
- boolean - True if the two colors are the same, False if different
502
503
EXAMPLES::
504
505
sage: Color('red') == Color((1,0,0))
506
True
507
sage: Color('blue') == Color((0,1,0))
508
False
509
sage: Color('blue') + Color((0,1,0)) == Color((0,0.5,0.5))
510
True
511
sage: Color(0.2,0.3,0.2) == False
512
False
513
"""
514
if isinstance(right, Color):
515
return self._rgb == right._rgb
516
else:
517
return False
518
519
def __ne__(self, right):
520
"""
521
Compare two :class:`Color` objects to determine whether
522
they refer to different colors.
523
524
INPUT:
525
526
- ``right`` - a :class:`Color` instance
527
528
OUTPUT:
529
530
- boolean - True if the two colors are different,
531
False if they're the same
532
533
EXAMPLES::
534
535
sage: Color('green') != Color('yellow')
536
True
537
sage: Color('red') != Color(1,0,0)
538
False
539
sage: Color('yellow') != Color(1,1,0)
540
False
541
sage: Color('blue') != 23
542
True
543
"""
544
return not (self == right)
545
546
def __gt__(self, right):
547
"""
548
Check whether a :class:`Color` object is greater than some other
549
object. This doesn't make sense, and so we conclude that it is
550
not greater than the other object.
551
552
INPUT:
553
554
- ``right`` - an object
555
556
OUTPUT:
557
558
- boolean - False
559
560
EXAMPLES::
561
562
sage: Color('red') > Color('red')
563
False
564
sage: Color('blue') > Color('red')
565
False
566
sage: Color('red') > "xyzzy"
567
False
568
"""
569
return False
570
571
def __ge__(self, right):
572
"""
573
Check whether a :class:`Color` object is greater than or equal
574
to some other object. It wouldn't make sense for it to be
575
greater than the other object, so we treat this the same as an
576
equality check.
577
578
INPUT:
579
580
- ``right`` - an object
581
582
OUTPUT:
583
584
- boolean - False
585
586
EXAMPLES::
587
588
sage: Color('red') >= Color('red')
589
True
590
sage: Color('blue') >= Color('red')
591
False
592
sage: Color('red') >= "xyzzy"
593
False
594
"""
595
return self == right
596
597
def __hash__(self):
598
"""
599
Return the hash value of a color.
600
Equal colors return equal hash values.
601
602
OUTPUT:
603
604
- a hash value
605
606
EXAMPLES::
607
608
sage: hash(Color('red')) # random
609
873150856
610
sage: hash(Color('red')) == hash(Color((1,0,0)))
611
True
612
"""
613
return hash(self._rgb)
614
615
def blend(self, color, fraction=0.5):
616
"""
617
Return a color blended with the given ``color`` by a given
618
``fraction``. The algorithm interpolates linearly between the
619
colors' corresponding R, G, and B coordinates.
620
621
INPUT:
622
623
- ``color`` - a :class:`Color` instance or float-convertible
624
3-tuple/list; the color with which to blend this color
625
626
- ``fraction`` - a float-convertible number; the fraction of
627
``color`` to blend with this color
628
629
OUTPUT:
630
631
- a **new** :class:`Color` instance
632
633
EXAMPLES::
634
635
sage: from sage.plot.colors import red, blue, lime
636
sage: red.blend(blue)
637
RGB color (0.5, 0.0, 0.5)
638
sage: red.blend(blue, fraction=0.0)
639
RGB color (1.0, 0.0, 0.0)
640
sage: red.blend(blue, fraction=1.0)
641
RGB color (0.0, 0.0, 1.0)
642
sage: lime.blend((0.3, 0.5, 0.7))
643
RGB color (0.15, 0.75, 0.35)
644
sage: blue.blend(blue)
645
RGB color (0.0, 0.0, 1.0)
646
sage: red.blend(lime, fraction=0.3)
647
RGB color (0.7, 0.3, 0.0)
648
sage: blue.blend((0.0, 0.9, 0.2), fraction=0.2)
649
RGB color (0.0, 0.18000000000000002, 0.8400000000000001)
650
sage: red.blend(0.2)
651
Traceback (most recent call last):
652
...
653
TypeError: 0.200000000000000 must be a Color or float-convertible 3-tuple/list
654
"""
655
fraction = float(fraction)
656
if isinstance(color, Color):
657
color = color._rgb
658
if isinstance(color, (list, tuple)) and len(color) == 3:
659
color = map(float, color)
660
return Color(rgbcolor([(1 - fraction) * a + fraction * b
661
for a, b in zip(self._rgb, color)]))
662
raise TypeError("%s must be a Color or float-convertible 3-tuple/list" % (color, ))
663
664
def __add__(self, right):
665
"""
666
Return a color "added" on the right to another color, with
667
:meth:`blend`.
668
669
INPUT:
670
671
- ``right`` - a :class:`Color` instance or float-convertible
672
3-tuple/list
673
674
OUTPUT:
675
676
- a **new** :class:`Color` instance
677
678
EXAMPLES::
679
680
sage: from sage.plot.colors import red, blue, lime
681
sage: red + blue + lime
682
RGB color (0.25, 0.5, 0.25)
683
sage: from sage.plot.colors import cyan, magenta, yellow
684
sage: cyan + magenta + yellow
685
RGB color (0.75, 0.75, 0.5)
686
sage: c1 = Color(0.1, 0.5, 0.8); c2 = Color(0.2, 0.4, 0.7, space='hsv')
687
sage: c1 + 0.1
688
Traceback (most recent call last):
689
...
690
TypeError: 0.100000000000000 must be a Color or float-convertible 3-tuple/list
691
sage: c2 + [0.5, 0.2, 0.9]
692
RGB color (0.572, 0.44999999999999996, 0.66)
693
sage: c1.__add__(red).__add__((0.9, 0.2, 1/3))
694
RGB color (0.7250000000000001, 0.225, 0.3666666666666667)
695
sage: c1 + c2
696
RGB color (0.37199999999999994, 0.6, 0.61)
697
"""
698
return self.blend(right)
699
700
def __radd__(self, left):
701
"""
702
Return a color "added" on the left to another color, with
703
:meth:`blend`.
704
705
INPUT:
706
707
- ``left`` - a :class:`Color` instance or float-convertible
708
3-tuple/list
709
710
OUTPUT:
711
712
- a **new** :class:`Color` instance
713
714
EXAMPLES::
715
716
sage: from sage.plot.colors import olive, orchid
717
sage: olive + orchid
718
RGB color (0.6784313725490196, 0.47058823529411764, 0.4196078431372549)
719
sage: d1 = Color(0.1, 0.5, 0.8, space='hls'); d2 = Color(0.2, 0.4, 0.7)
720
sage: [0.5, 0.2, 0.9] + d2
721
RGB color (0.35, 0.30000000000000004, 0.8)
722
sage: 0.1 + d1
723
Traceback (most recent call last):
724
...
725
TypeError: 0.100000000000000 must be a Color or float-convertible 3-tuple/list
726
sage: d2.__radd__(Color('brown')).__radd__((0.9, 0.2, 1/3))
727
RGB color (0.661764705882353, 0.2411764705882353, 0.38284313725490193)
728
"""
729
return self + left
730
731
def __mul__(self, right):
732
"""
733
Return a color whose RGB coordinates are this color's
734
coordinates multiplied on the right by a scalar.
735
736
INPUT:
737
738
- ``right`` - a float-convertible number
739
740
OUTPUT:
741
742
- a **new** :class:`Color` instance
743
744
EXAMPLES::
745
746
sage: Color('yellow') * 0.5
747
RGB color (0.5, 0.5, 0.0)
748
sage: Color('yellow') * (9.0 / 8.0) # reduced modulo 1.0
749
RGB color (0.125, 0.125, 0.0)
750
sage: from sage.plot.colors import cyan, grey, indianred
751
sage: cyan * 0.3 + grey * 0.1 + indianred * 0.6
752
RGB color (0.25372549019607843, 0.1957843137254902, 0.1957843137254902)
753
sage: indianred.__mul__(42)
754
RGB color (0.764705882352942, 0.1529411764705877, 0.1529411764705877)
755
"""
756
right = float(right)
757
return Color([x * right for x in self._rgb])
758
759
def __rmul__(self, left):
760
"""
761
Return a color whose RGB coordinates are this color's
762
coordinates multiplied on the left by a scalar.
763
764
INPUT:
765
766
- ``left`` - a float-convertible number
767
768
OUTPUT:
769
770
- a **new** :class:`Color` instance
771
772
EXAMPLES::
773
774
sage: from sage.plot.colors import aqua, cornsilk, tomato
775
sage: 0.3 * aqua
776
RGB color (0.0, 0.3, 0.3)
777
sage: Color('indianred').__rmul__(42)
778
RGB color (0.764705882352942, 0.1529411764705877, 0.1529411764705877)
779
"""
780
return self * left
781
782
def __div__(self, right):
783
"""
784
Return a color whose RGB coordinates are this color's
785
coordinates divided by a scalar. This method is called for
786
"classic division."
787
788
INPUT:
789
790
- ``right`` - a float-convertible, non-zero number
791
792
OUTPUT:
793
794
- a **new** instance of :class:`Color`
795
796
EXAMPLES::
797
798
sage: from sage.plot.colors import papayawhip, yellow
799
sage: yellow / 4
800
RGB color (0.25, 0.25, 0.0)
801
sage: yellow.__div__(4)
802
RGB color (0.25, 0.25, 0.0)
803
sage: (papayawhip + Color(0.5, 0.5, 0.1) + yellow) / 3.0
804
RGB color (0.29166666666666663, 0.286437908496732, 0.07794117647058824)
805
sage: vector((papayawhip / 2).rgb()) == vector((papayawhip * 0.5).rgb())
806
True
807
sage: yellow.__div__(1/4)
808
RGB color (0.0, 0.0, 0.0)
809
sage: Color('black') / 0.0
810
Traceback (most recent call last):
811
...
812
ZeroDivisionError: float division by zero
813
sage: papayawhip / yellow
814
Traceback (most recent call last):
815
...
816
TypeError: float() argument must be a string or a number
817
"""
818
return self * (float(1.0) / float(right))
819
820
def __truediv__(self, right):
821
"""
822
Return a color whose RGB coordinates are this color's
823
coordinates divided by a scalar. This method is called for
824
"true division."
825
826
INPUT:
827
828
- ``right`` - a float-convertible, non-zero number
829
830
OUTPUT:
831
832
- a **new** instance of :class:`Color`
833
834
EXAMPLES::
835
836
sage: from __future__ import division
837
sage: from sage.plot.colors import yellow, gold
838
sage: yellow / 4
839
RGB color (0.25, 0.25, 0.0)
840
sage: yellow.__truediv__(4)
841
RGB color (0.25, 0.25, 0.0)
842
sage: gold / pi + yellow * e
843
RGB color (0.51829585732141..., 0.49333037605210..., 0.0)
844
"""
845
return self.__div__(right)
846
847
def __hex__(self):
848
"""
849
Return a hexadecimal string representation of this color.
850
This is just the color's HTML hex representation without the
851
leading '#'.
852
853
OUTPUT:
854
855
- a string of length 6
856
857
EXAMPLES::
858
859
sage: from sage.plot.colors import whitesmoke
860
sage: hex(whitesmoke)
861
'f5f5f5'
862
sage: whitesmoke.html_color() == '#' + hex(whitesmoke)
863
True
864
sage: hex(Color(0.5, 1.0, 1.0, space='hsv'))
865
'00ffff'
866
sage: set([len(hex(Color(t, 1-t, t * t))) for t in srange(0, 1, 0.1)])
867
set([6])
868
"""
869
return self.html_color()[1:]
870
871
def __iter__(self):
872
"""
873
Return an iterator over the RGB coordinates of this color.
874
875
OUTPUT:
876
877
- a tupleiterator
878
879
EXAMPLES::
880
881
sage: from sage.plot.colors import dodgerblue, maroon
882
sage: r, g, b = dodgerblue
883
sage: r
884
0.11764705882352941
885
sage: g
886
0.5647058823529412
887
sage: b
888
1.0
889
sage: vector(maroon) == vector(Color(maroon)) == vector(Color('maroon'))
890
True
891
"""
892
return iter(self._rgb)
893
894
def __getitem__(self, i):
895
"""
896
Return the Red (0th), Green (1st), or Blue (2nd) coordinate of this
897
color via index access.
898
899
INPUT:
900
901
- ``i`` - an integer; the 0-based coordinate to retrieve
902
903
OUTPUT:
904
905
- a float
906
907
EXAMPLES::
908
909
sage: from sage.plot.colors import crimson, midnightblue
910
sage: Color('#badfad')[0]
911
0.7294117647058823
912
sage: (crimson[0], crimson[1], crimson[2]) == crimson.rgb()
913
True
914
sage: midnightblue[2] == midnightblue[-1]
915
True
916
sage: midnightblue[3]
917
Traceback (most recent call last):
918
...
919
IndexError: tuple index out of range
920
"""
921
return self._rgb[i]
922
923
def rgb(self):
924
"""
925
Return the underlying Red-Green-Blue (RGB) coordinates of this
926
color.
927
928
OUTPUT:
929
930
- a 3-tuple of floats
931
932
EXAMPLES::
933
934
sage: Color(0.3, 0.5, 0.7).rgb()
935
(0.3, 0.5, 0.7)
936
sage: Color('#8000ff').rgb()
937
(0.5019607843137255, 0.0, 1.0)
938
sage: from sage.plot.colors import orange
939
sage: orange.rgb()
940
(1.0, 0.6470588235294118, 0.0)
941
sage: Color('magenta').rgb()
942
(1.0, 0.0, 1.0)
943
sage: Color(1, 0.7, 0.9, space='hsv').rgb()
944
(0.9, 0.2700000000000001, 0.2700000000000001)
945
"""
946
return self._rgb
947
948
def hls(self):
949
"""
950
Return the Hue-Lightness-Saturation (HLS) coordinates of this
951
color.
952
953
OUTPUT:
954
955
- a 3-tuple of floats
956
957
EXAMPLES::
958
959
sage: Color(0.3, 0.5, 0.7, space='hls').hls()
960
(0.30000000000000004, 0.5, 0.7)
961
sage: Color(0.3, 0.5, 0.7, space='hsl').hls()
962
(0.30000000000000004, 0.7, 0.5000000000000001)
963
sage: Color('#aabbcc').hls()
964
(0.5833333333333334, 0.7333333333333334, 0.25000000000000017)
965
sage: from sage.plot.colors import orchid
966
sage: orchid.hls()
967
(0.8396226415094339, 0.6470588235294117, 0.5888888888888889)
968
"""
969
return tuple(map(float, rgb_to_hls(*self._rgb)))
970
971
def hsl(self):
972
"""
973
Return the Hue-Saturation-Lightness (HSL) coordinates of this
974
color.
975
976
OUTPUT:
977
978
- a 3-tuple of floats
979
980
EXAMPLES::
981
982
sage: Color(1,0,0).hsl()
983
(0.0, 1.0, 0.5)
984
sage: from sage.plot.colors import orchid
985
sage: orchid.hsl()
986
(0.8396226415094339, 0.5888888888888889, 0.6470588235294117)
987
sage: Color('#aabbcc').hsl()
988
(0.5833333333333334, 0.25000000000000017, 0.7333333333333334)
989
"""
990
h, l, s = tuple(map(float, rgb_to_hls(*self._rgb)))
991
return (h, s, l)
992
993
def hsv(self):
994
"""
995
Return the Hue-Saturation-Value (HSV) coordinates of this
996
color.
997
998
OUTPUT:
999
1000
- a 3-tuple of floats
1001
1002
EXAMPLES::
1003
1004
sage: from sage.plot.colors import red
1005
sage: red.hsv()
1006
(0.0, 1.0, 1.0)
1007
sage: Color(1,1,1).hsv()
1008
(0.0, 0.0, 1.0)
1009
sage: Color('gray').hsv()
1010
(0.0, 0.0, 0.5019607843137255)
1011
"""
1012
return tuple(map(float, rgb_to_hsv(*self._rgb)))
1013
1014
def html_color(self):
1015
"""
1016
Return a HTML hex representation for this color.
1017
1018
OUTPUT:
1019
1020
- a string of length 7.
1021
1022
EXAMPLES::
1023
1024
sage: Color('yellow').html_color()
1025
'#ffff00'
1026
sage: Color('#fedcba').html_color()
1027
'#fedcba'
1028
sage: Color(0.0, 1.0, 0.0).html_color()
1029
'#00ff00'
1030
sage: from sage.plot.colors import honeydew
1031
sage: honeydew.html_color()
1032
'#f0fff0'
1033
"""
1034
return float_to_html(*self._rgb)
1035
1036
def lighter(self, fraction=1.0/3.0):
1037
"""
1038
Return a lighter "shade" of this RGB color by
1039
:meth:`blend`-ing it with white. This is **not** an inverse
1040
of :meth:`darker`.
1041
1042
INPUT:
1043
1044
- ``fraction`` - a float (default: 1.0/3.0); blending fraction
1045
to apply
1046
1047
OUTPUT:
1048
1049
- a **new** instance of :class:`Color`
1050
1051
EXAMPLES::
1052
1053
sage: from sage.plot.colors import khaki
1054
sage: khaki.lighter()
1055
RGB color (0.9607843137254903, 0.934640522875817, 0.6993464052287582)
1056
sage: Color('white').lighter().darker()
1057
RGB color (0.6666666666666667, 0.6666666666666667, 0.6666666666666667)
1058
sage: Color('#abcdef').lighter(1/4)
1059
RGB color (0.7529411764705882, 0.8529411764705883, 0.9529411764705882)
1060
sage: Color(1, 0, 8/9, space='hsv').lighter()
1061
RGB color (0.925925925925926, 0.925925925925926, 0.925925925925926)
1062
"""
1063
return self.blend((1.0, 1.0, 1.0), fraction)
1064
1065
def darker(self, fraction=1.0/3.0):
1066
"""
1067
Return a darker "shade" of this RGB color by :meth:`blend`-ing
1068
it with black. This is **not** an inverse of :meth:`lighter`.
1069
1070
INPUT:
1071
1072
- ``fraction`` - a float (default: 1.0/3.0); blending fraction
1073
to apply
1074
1075
OUTPUT:
1076
1077
- a new instance of :class:`Color`
1078
1079
EXAMPLES::
1080
1081
sage: from sage.plot.colors import black
1082
sage: vector(black.darker().rgb()) == vector(black.rgb())
1083
True
1084
sage: Color(0.4, 0.6, 0.8).darker(0.1)
1085
RGB color (0.36000000000000004, 0.54, 0.7200000000000001)
1086
sage: Color(.1,.2,.3,space='hsl').darker()
1087
RGB color (0.24000000000000002, 0.20800000000000002, 0.16)
1088
"""
1089
return self.blend((0.0, 0.0, 0.0), fraction)
1090
1091
1092
class ColorsDict(dict):
1093
"""
1094
A dict-like collection of colors, accessible via key or attribute.
1095
For a list of color names, evaluate::
1096
1097
sage: sorted(colors)
1098
['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', ...]
1099
"""
1100
def __init__(self):
1101
"""
1102
Constructs a dict-like collection of colors. The keys are the
1103
color names (i.e., strings) and the values are RGB 3-tuples of
1104
floats.
1105
1106
EXAMPLES::
1107
1108
sage: from sage.plot.colors import ColorsDict
1109
sage: cols = ColorsDict()
1110
sage: set([(type(c), type(cols[c])) for c in cols])
1111
set([(<type 'str'>, <class 'sage.plot.colors.Color'>)])
1112
sage: sorted(cols)
1113
['aliceblue', 'antiquewhite', 'aqua', 'aquamarine', ...]
1114
sage: len(cols)
1115
148
1116
"""
1117
# Convert the colors_dict defined above to Color instances.
1118
for k in colors_dict:
1119
self[k] = Color(colors_dict[k])
1120
1121
def __getattr__(self, name):
1122
"""
1123
Gets a color via attribute access.
1124
1125
INPUT:
1126
1127
- ``name`` - a string; the name of the color to return
1128
1129
OUTPUT:
1130
1131
- a RGB 3-tuple of floats
1132
1133
EXAMPLES::
1134
1135
sage: from sage.plot.colors import ColorsDict, blue
1136
sage: cols = ColorsDict()
1137
sage: cols.blue
1138
RGB color (0.0, 0.0, 1.0)
1139
sage: cols['blue']
1140
RGB color (0.0, 0.0, 1.0)
1141
sage: blue
1142
RGB color (0.0, 0.0, 1.0)
1143
sage: cols.punk
1144
Traceback (most recent call last):
1145
...
1146
AttributeError: 'ColorsDict' has no attribute or colormap punk
1147
"""
1148
try:
1149
return self.__getitem__(name)
1150
except KeyError:
1151
raise AttributeError("'%s' has no attribute or colormap %s"%(type(self).__name__,name))
1152
1153
def __dir__(self):
1154
"""
1155
Returns an approximate list of attribute names, including the
1156
color names.
1157
1158
OUTPUT:
1159
1160
- a list of strings
1161
1162
EXAMPLES::
1163
1164
sage: from sage.plot.colors import ColorsDict
1165
sage: cols = ColorsDict()
1166
sage: 'green' in dir(cols)
1167
True
1168
"""
1169
methods = ['__dir__', '__getattr__']
1170
return dir(super(ColorsDict, self)) + methods + self.keys()
1171
1172
colors = ColorsDict()
1173
1174
# Add convenient module-scope colors. These are Color instances.
1175
for c in colors:
1176
vars()[c] = colors[c]
1177
1178
1179
def hue(h, s=1, v=1):
1180
r"""
1181
Convert a Hue-Saturation-Value (HSV) color tuple to a valid
1182
Red-Green-Blue (RGB) tuple. All three inputs should lie in the
1183
interval [0.0, 1.0]; otherwise, they are reduced modulo 1 (see
1184
:func:`mod_one`). In particular ``h=0`` and ``h=1`` yield red,
1185
with the intermediate hues orange, yellow, green, cyan, blue, and
1186
violet as ``h`` increases.
1187
1188
This function makes it easy to sample a broad range of colors for
1189
graphics::
1190
1191
sage: p = Graphics()
1192
sage: for phi in xsrange(0, 2 * pi, 1 / pi):
1193
... p += plot(sin(x + phi), (x, -7, 7), rgbcolor = hue(phi))
1194
sage: p
1195
1196
INPUT:
1197
1198
- ``h`` - a number; the color's hue
1199
1200
- ``s`` - a number (default: 1); the color's saturation
1201
1202
- ``v`` - a number (default: 1); the color's value
1203
1204
OUTPUT:
1205
1206
- a RGB 3-tuple of floats in the interval [0.0, 1.0]
1207
1208
EXAMPLES::
1209
1210
sage: hue(0.6)
1211
(0.0, 0.40000000000000036, 1.0)
1212
sage: from sage.plot.colors import royalblue
1213
sage: royalblue
1214
RGB color (0.2549019607843137, 0.4117647058823529, 0.8823529411764706)
1215
sage: hue(*royalblue.hsv())
1216
(0.2549019607843137, 0.4117647058823529, 0.8823529411764706)
1217
sage: hue(.5, .5, .5)
1218
(0.25, 0.5, 0.5)
1219
1220
.. note :: The HSV to RGB coordinate transformation itself is
1221
given in the source code for the Python library's
1222
:mod:`colorsys` module::
1223
1224
sage: from colorsys import hsv_to_rgb # not tested
1225
sage: hsv_to_rgb?? # not tested
1226
"""
1227
return tuple(map(float, hsv_to_rgb(mod_one(h), mod_one(s), mod_one(v))))
1228
1229
1230
def float_to_html(r, g, b):
1231
"""
1232
Converts a Red-Green-Blue (RGB) color tuple to a HTML hex color.
1233
Each input value should be in the interval [0.0, 1.0]; otherwise,
1234
the values are first reduced modulo one (see :func:`mod_one`).
1235
1236
INPUT:
1237
1238
- ``r`` - a number; the RGB color's "red" intensity
1239
1240
- ``g`` - a number; the RGB color's "green" intensity
1241
1242
- ``b`` - a number; the RGB color's "blue" intensity
1243
1244
OUTPUT:
1245
1246
- a string of length 7, starting with '#'
1247
1248
EXAMPLES::
1249
1250
sage: from sage.plot.colors import float_to_html
1251
sage: float_to_html(1.,1.,0.)
1252
'#ffff00'
1253
sage: float_to_html(.03,.06,.02)
1254
'#070f05'
1255
sage: float_to_html(*Color('brown').rgb())
1256
'#a52a2a'
1257
sage: float_to_html((0.2, 0.6, 0.8))
1258
Traceback (most recent call last):
1259
...
1260
TypeError: float_to_html() takes exactly 3 arguments (1 given)
1261
"""
1262
# TODO: figure out why this is necessary
1263
from sage.rings.integer import Integer
1264
from math import floor
1265
1266
r, g, b = map(mod_one, (r, g, b))
1267
rr = Integer(int(floor(r * 255))).str(base = 16)
1268
gg = Integer(int(floor(g * 255))).str(base = 16)
1269
bb = Integer(int(floor(b * 255))).str(base = 16)
1270
1271
rr = '0' * (2 - len(rr)) + rr
1272
gg = '0' * (2 - len(gg)) + gg
1273
bb = '0' * (2 - len(bb)) + bb
1274
1275
return '#' + rr + gg + bb
1276
1277
1278
def rainbow(n, format='hex'):
1279
"""
1280
Returns a list of colors sampled at equal intervals over the
1281
spectrum, from Hue-Saturation-Value (HSV) coordinates (0, 1, 1) to
1282
(1, 1, 1). This range is red at the extremes, but it covers
1283
orange, yellow, green, cyan, blue, violet, and many other hues in
1284
between. This function is particularly useful for representing
1285
vertex partitions on graphs.
1286
1287
INPUT:
1288
1289
- ``n`` - a number; the length of the list
1290
1291
- ``format`` - a string (default: 'hex'); the output format for
1292
each color in the list; the other choice is 'rgbtuple'
1293
1294
OUTPUT:
1295
1296
- a list of strings or RGB 3-tuples of floats in the interval
1297
[0.0, 1.0]
1298
1299
EXAMPLES::
1300
1301
sage: from sage.plot.colors import rainbow
1302
sage: rainbow(7)
1303
['#ff0000', '#ffda00', '#48ff00', '#00ff91', '#0091ff', '#4800ff', '#ff00da']
1304
sage: rainbow(7, 'rgbtuple')
1305
[(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.2857142857142856, 0.0, 1.0), (1.0, 0.0, 0.8571428571428577)]
1306
1307
AUTHORS:
1308
1309
- Robert L. Miller
1310
1311
- Karl-Dieter Crisman (directly use :func:`hsv_to_rgb` for hues)
1312
"""
1313
from sage.rings.integer import Integer
1314
n = Integer(n) # In case n is a Python int and i/n below would give 0!
1315
R = []
1316
1317
for i in range(n):
1318
R.append(tuple(map(float, hsv_to_rgb(i / n, 1, 1))))
1319
1320
if format == 'rgbtuple':
1321
return R
1322
elif format == 'hex':
1323
for j in range(len(R)):
1324
R[j] = float_to_html(*R[j])
1325
return R
1326
1327
1328
# If you change what this accepts, remember to change the documentation
1329
# about cmap where it is used and to test these classes.
1330
def get_cmap(cmap):
1331
r"""
1332
Returns a color map (actually, a matplotlib :class:`Colormap`
1333
object), given its name or a [mixed] list/tuple of RGB list/tuples
1334
and color names. For a list of map names, evaluate::
1335
1336
sage: sorted(colormaps)
1337
['Accent', 'Accent_r', 'Blues', 'Blues_r', ...]
1338
1339
See :func:`rgbcolor` for valid list/tuple element formats.
1340
1341
INPUT:
1342
1343
- ``cmap`` - a string, list, tuple, or
1344
:class:`matplotlib.colors.Colormap`; a string must be a valid
1345
color map name
1346
1347
OUTPUT:
1348
1349
- a :class:`matplotlib.colors.Colormap` instance
1350
1351
EXAMPLES::
1352
1353
sage: from sage.plot.colors import get_cmap
1354
sage: get_cmap('jet')
1355
<matplotlib.colors.LinearSegmentedColormap object at 0x...>
1356
sage: get_cmap(u'jet')
1357
<matplotlib.colors.LinearSegmentedColormap object at 0x...>
1358
sage: get_cmap([(0,0,0), (0.5,0.5,0.5), (1,1,1)])
1359
<matplotlib.colors.ListedColormap object at 0x...>
1360
sage: get_cmap(['green', 'lightblue', 'blue'])
1361
<matplotlib.colors.ListedColormap object at 0x...>
1362
sage: get_cmap(((0.5, 0.3, 0.2), [1.0, 0.0, 0.5], 'purple', Color(0.5,0.5,1, space='hsv')))
1363
<matplotlib.colors.ListedColormap object at 0x...>
1364
sage: get_cmap('jolies')
1365
Traceback (most recent call last):
1366
...
1367
RuntimeError: Color map jolies not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)
1368
sage: get_cmap('mpl')
1369
Traceback (most recent call last):
1370
...
1371
RuntimeError: Color map mpl not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)
1372
"""
1373
# matplotlib color maps
1374
global cm
1375
if not cm:
1376
from matplotlib import cm
1377
from matplotlib.colors import ListedColormap, Colormap
1378
1379
if isinstance(cmap, Colormap):
1380
return cmap
1381
1382
elif isinstance(cmap, basestring):
1383
if not cmap in cm.datad.keys():
1384
raise RuntimeError("Color map %s not known (type import matplotlib.cm; matplotlib.cm.datad.keys() for valid names)" % cmap)
1385
return cm.__dict__[cmap]
1386
1387
elif isinstance(cmap, (list, tuple)):
1388
cmap = map(rgbcolor, cmap)
1389
return ListedColormap(cmap)
1390
1391
1392
class Colormaps(collections.MutableMapping):
1393
"""
1394
A dict-like collection of lazily-loaded matplotlib color maps.
1395
For a list of map names, evaluate::
1396
1397
sage: sorted(colormaps)
1398
['Accent', 'Accent_r', 'Blues', 'Blues_r', ...]
1399
"""
1400
def __init__(self):
1401
"""
1402
Constructs an empty mutable collection of color maps.
1403
1404
EXAMPLES::
1405
1406
sage: from sage.plot.colors import Colormaps
1407
sage: maps = Colormaps()
1408
sage: len(maps.maps)
1409
0
1410
"""
1411
self.maps = {}
1412
1413
def load_maps(self):
1414
"""
1415
If it's necessary, loads matplotlib's color maps and adds them
1416
to the collection.
1417
1418
EXAMPLES::
1419
1420
sage: from sage.plot.colors import Colormaps
1421
sage: maps = Colormaps()
1422
sage: len(maps.maps)
1423
0
1424
sage: maps.load_maps()
1425
sage: len(maps.maps)>130
1426
True
1427
"""
1428
global cm
1429
if not cm:
1430
from matplotlib import cm
1431
if not self.maps:
1432
for cmap in cm.datad.keys():
1433
self.maps[cmap] = cm.__getattribute__(cmap)
1434
1435
def __dir__(self):
1436
"""
1437
Returns an approximate list of attribute names, including the
1438
color map names.
1439
1440
OUTPUT:
1441
1442
- a list of strings
1443
1444
EXAMPLES::
1445
1446
sage: from sage.plot.colors import Colormaps
1447
sage: maps = Colormaps()
1448
sage: 'Accent' in dir(maps)
1449
True
1450
"""
1451
self.load_maps()
1452
methods = ['load_maps', '__dir__', '__len__', '__iter__',
1453
'__contains__', '__getitem__', '__getattr__',
1454
'__setitem__', '__delitem__']
1455
return dir(super(Colormaps, self)) + methods + self.keys()
1456
1457
def __len__(self):
1458
"""
1459
Returns the number of color maps.
1460
1461
OUTPUT:
1462
1463
- an int
1464
1465
EXAMPLES::
1466
1467
sage: from sage.plot.colors import Colormaps
1468
sage: maps = Colormaps()
1469
sage: len(maps)>130
1470
True
1471
"""
1472
self.load_maps()
1473
return len(self.maps)
1474
1475
def __iter__(self):
1476
"""
1477
Returns an iterator over the color map collection.
1478
1479
OUTPUT:
1480
1481
- a dictionary key iterator instance
1482
1483
EXAMPLES::
1484
1485
sage: from sage.plot.colors import Colormaps
1486
sage: maps = Colormaps()
1487
sage: count = 0
1488
sage: for m in maps: count += 1
1489
sage: count == len(maps)
1490
True
1491
"""
1492
self.load_maps()
1493
return iter(self.maps)
1494
1495
def __contains__(self, name):
1496
"""
1497
Returns whether a map is in the color maps collection.
1498
1499
INPUT:
1500
1501
- ``name`` - a string; the name of the map to query
1502
1503
OUTPUT:
1504
1505
- a boolean
1506
1507
EXAMPLES::
1508
1509
sage: from sage.plot.colors import Colormaps
1510
sage: maps = Colormaps()
1511
sage: 'summer' in maps
1512
True
1513
sage: 'not really a color map' in maps
1514
False
1515
"""
1516
self.load_maps()
1517
return name in self.maps
1518
1519
def __getitem__(self, name):
1520
"""
1521
Gets a color map from the collection via key access.
1522
1523
INPUT:
1524
1525
- ``name`` - a string; the name of the map return
1526
1527
OUTPUT:
1528
1529
- an instance of :class:`matplotlib.colors.Colormap`
1530
1531
EXAMPLES::
1532
1533
sage: from sage.plot.colors import Colormaps
1534
sage: maps = Colormaps()
1535
sage: maps.get('Oranges')
1536
<matplotlib.colors.LinearSegmentedColormap object at ...>
1537
sage: maps['copper']
1538
<matplotlib.colors.LinearSegmentedColormap object at ...>
1539
sage: maps.get('not a color map')
1540
sage: maps['not a color map']
1541
Traceback (most recent call last):
1542
...
1543
KeyError: "no colormap with name 'not a color map'"
1544
"""
1545
self.load_maps()
1546
try:
1547
return self.maps[name]
1548
except KeyError:
1549
raise KeyError("no colormap with name '%s'" % name)
1550
1551
def __getattr__(self, name):
1552
"""
1553
Gets a color map from the collection via attribute access.
1554
1555
INPUT:
1556
1557
- ``name`` - a string; the name of the map to return
1558
1559
OUTPUT:
1560
1561
- an instance of :class:`matplotlib.colors.Colormap`
1562
1563
EXAMPLES::
1564
1565
sage: from sage.plot.colors import Colormaps
1566
sage: maps = Colormaps()
1567
sage: maps.pink
1568
<matplotlib.colors.LinearSegmentedColormap object at ...>
1569
sage: maps.punk
1570
Traceback (most recent call last):
1571
...
1572
AttributeError: 'Colormaps' has no attribute or colormap punk
1573
sage: maps['punk']
1574
Traceback (most recent call last):
1575
...
1576
KeyError: "no colormap with name 'punk'"
1577
sage: maps['bone'] == maps.bone
1578
True
1579
"""
1580
try:
1581
return self.__getitem__(name)
1582
except KeyError:
1583
raise AttributeError("'%s' has no attribute or colormap %s"%(type(self).__name__,name))
1584
1585
def __repr__(self):
1586
"""
1587
Returns a string representation of the color map collection.
1588
1589
OUTPUT:
1590
1591
- a string
1592
1593
EXAMPLES::
1594
1595
sage: from sage.plot.colors import Colormaps
1596
sage: maps = Colormaps()
1597
sage: maps
1598
{...}
1599
sage: type(repr(maps))
1600
<type 'str'>
1601
"""
1602
self.load_maps()
1603
return repr(self.maps)
1604
1605
def __setitem__(self, name, colormap):
1606
"""
1607
Adds a color map to the collection.
1608
1609
INPUT:
1610
1611
- ``name`` - a string; the name of the map to add
1612
1613
- ``colormap`` - an instance of
1614
:class:`matplotlib.colors.Colormap`; the color map to add
1615
1616
EXAMPLES::
1617
1618
sage: from sage.plot.colors import Colormaps, get_cmap
1619
sage: maps = Colormaps()
1620
sage: count = len(maps)
1621
sage: my_map = get_cmap(['chartreuse', '#007', (1.0, 0.0, 0.0)])
1622
sage: maps['my_map'] = my_map
1623
sage: 'my_map' in maps
1624
True
1625
sage: count + 1 == len(maps)
1626
True
1627
"""
1628
self.load_maps()
1629
self.maps[name] = colormap
1630
1631
def __delitem__(self, name):
1632
"""
1633
Removes a color map from the collection.
1634
1635
INPUT:
1636
1637
- ``name`` - a string; the name of the map to remove
1638
1639
EXAMPLES::
1640
1641
sage: from sage.plot.colors import Colormaps
1642
sage: maps = Colormaps()
1643
sage: count = len(maps)
1644
sage: maps.popitem()
1645
('Spectral', <matplotlib.colors.LinearSegmentedColormap object at ...>)
1646
sage: count - 1 == len(maps)
1647
True
1648
"""
1649
self.load_maps()
1650
del self.maps[name]
1651
1652
colormaps = Colormaps()
1653
1654