passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-plot might be problematic. Click here for more details.

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