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.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
- passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_plot.py +15 -0
- sage/ext_data/threejs/animation.css +195 -0
- sage/ext_data/threejs/animation.html +85 -0
- sage/ext_data/threejs/animation.js +273 -0
- sage/ext_data/threejs/fat_lines.js +48 -0
- sage/ext_data/threejs/threejs-version.txt +1 -0
- sage/ext_data/threejs/threejs_template.html +597 -0
- sage/interfaces/all__sagemath_plot.py +1 -0
- sage/interfaces/gnuplot.py +196 -0
- sage/interfaces/jmoldata.py +208 -0
- sage/interfaces/povray.py +56 -0
- sage/plot/all.py +42 -0
- sage/plot/animate.py +1796 -0
- sage/plot/arc.py +504 -0
- sage/plot/arrow.py +671 -0
- sage/plot/bar_chart.py +205 -0
- sage/plot/bezier_path.py +400 -0
- sage/plot/circle.py +435 -0
- sage/plot/colors.py +1606 -0
- sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/complex_plot.pyx +1446 -0
- sage/plot/contour_plot.py +1792 -0
- sage/plot/density_plot.py +318 -0
- sage/plot/disk.py +373 -0
- sage/plot/ellipse.py +375 -0
- sage/plot/graphics.py +3580 -0
- sage/plot/histogram.py +354 -0
- sage/plot/hyperbolic_arc.py +404 -0
- sage/plot/hyperbolic_polygon.py +416 -0
- sage/plot/hyperbolic_regular_polygon.py +296 -0
- sage/plot/line.py +626 -0
- sage/plot/matrix_plot.py +629 -0
- sage/plot/misc.py +509 -0
- sage/plot/multigraphics.py +1294 -0
- sage/plot/plot.py +4183 -0
- sage/plot/plot3d/all.py +23 -0
- sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/base.pxd +12 -0
- sage/plot/plot3d/base.pyx +3378 -0
- sage/plot/plot3d/implicit_plot3d.py +659 -0
- sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/index_face_set.pxd +32 -0
- sage/plot/plot3d/index_face_set.pyx +1873 -0
- sage/plot/plot3d/introduction.py +131 -0
- sage/plot/plot3d/list_plot3d.py +649 -0
- sage/plot/plot3d/parametric_plot3d.py +1130 -0
- sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/parametric_surface.pxd +12 -0
- sage/plot/plot3d/parametric_surface.pyx +893 -0
- sage/plot/plot3d/platonic.py +601 -0
- sage/plot/plot3d/plot3d.py +1442 -0
- sage/plot/plot3d/plot_field3d.py +162 -0
- sage/plot/plot3d/point_c.pxi +148 -0
- sage/plot/plot3d/revolution_plot3d.py +309 -0
- sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/shapes.pxd +22 -0
- sage/plot/plot3d/shapes.pyx +1382 -0
- sage/plot/plot3d/shapes2.py +1512 -0
- sage/plot/plot3d/tachyon.py +1779 -0
- sage/plot/plot3d/texture.py +453 -0
- sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- 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()
|