passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.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 (82) hide show
  1. passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
  2. passagemath_plot-10.6.31rc3.dist-info/RECORD +82 -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-83c28eba.so.5.0.0 +0 -0
  6. passagemath_plot.libs/libgsl-cda90e79.so.28.0.0 +0 -0
  7. passagemath_plot.libs/libopenblasp-r0-6dcb67f9.3.29.so +0 -0
  8. passagemath_plot.libs/libquadmath-2284e583.so.0.0.0 +0 -0
  9. sage/all__sagemath_plot.py +15 -0
  10. sage/ext_data/threejs/animation.css +195 -0
  11. sage/ext_data/threejs/animation.html +85 -0
  12. sage/ext_data/threejs/animation.js +273 -0
  13. sage/ext_data/threejs/fat_lines.js +48 -0
  14. sage/ext_data/threejs/threejs-version.txt +1 -0
  15. sage/ext_data/threejs/threejs_template.html +597 -0
  16. sage/interfaces/all__sagemath_plot.py +1 -0
  17. sage/interfaces/gnuplot.py +196 -0
  18. sage/interfaces/jmoldata.py +208 -0
  19. sage/interfaces/povray.py +56 -0
  20. sage/plot/all.py +42 -0
  21. sage/plot/animate.py +1796 -0
  22. sage/plot/arc.py +504 -0
  23. sage/plot/arrow.py +671 -0
  24. sage/plot/bar_chart.py +205 -0
  25. sage/plot/bezier_path.py +400 -0
  26. sage/plot/circle.py +435 -0
  27. sage/plot/colors.py +1606 -0
  28. sage/plot/complex_plot.cpython-314-x86_64-linux-gnu.so +0 -0
  29. sage/plot/complex_plot.pyx +1446 -0
  30. sage/plot/contour_plot.py +1792 -0
  31. sage/plot/density_plot.py +318 -0
  32. sage/plot/disk.py +373 -0
  33. sage/plot/ellipse.py +375 -0
  34. sage/plot/graphics.py +3580 -0
  35. sage/plot/histogram.py +354 -0
  36. sage/plot/hyperbolic_arc.py +404 -0
  37. sage/plot/hyperbolic_polygon.py +416 -0
  38. sage/plot/hyperbolic_regular_polygon.py +296 -0
  39. sage/plot/line.py +626 -0
  40. sage/plot/matrix_plot.py +629 -0
  41. sage/plot/misc.py +509 -0
  42. sage/plot/multigraphics.py +1294 -0
  43. sage/plot/plot.py +4183 -0
  44. sage/plot/plot3d/all.py +23 -0
  45. sage/plot/plot3d/base.cpython-314-x86_64-linux-gnu.so +0 -0
  46. sage/plot/plot3d/base.pxd +12 -0
  47. sage/plot/plot3d/base.pyx +3378 -0
  48. sage/plot/plot3d/implicit_plot3d.py +659 -0
  49. sage/plot/plot3d/implicit_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  50. sage/plot/plot3d/implicit_surface.pyx +1453 -0
  51. sage/plot/plot3d/index_face_set.cpython-314-x86_64-linux-gnu.so +0 -0
  52. sage/plot/plot3d/index_face_set.pxd +32 -0
  53. sage/plot/plot3d/index_face_set.pyx +1873 -0
  54. sage/plot/plot3d/introduction.py +131 -0
  55. sage/plot/plot3d/list_plot3d.py +649 -0
  56. sage/plot/plot3d/parametric_plot3d.py +1130 -0
  57. sage/plot/plot3d/parametric_surface.cpython-314-x86_64-linux-gnu.so +0 -0
  58. sage/plot/plot3d/parametric_surface.pxd +12 -0
  59. sage/plot/plot3d/parametric_surface.pyx +893 -0
  60. sage/plot/plot3d/platonic.py +601 -0
  61. sage/plot/plot3d/plot3d.py +1442 -0
  62. sage/plot/plot3d/plot_field3d.py +162 -0
  63. sage/plot/plot3d/point_c.pxi +148 -0
  64. sage/plot/plot3d/revolution_plot3d.py +309 -0
  65. sage/plot/plot3d/shapes.cpython-314-x86_64-linux-gnu.so +0 -0
  66. sage/plot/plot3d/shapes.pxd +22 -0
  67. sage/plot/plot3d/shapes.pyx +1382 -0
  68. sage/plot/plot3d/shapes2.py +1512 -0
  69. sage/plot/plot3d/tachyon.py +1779 -0
  70. sage/plot/plot3d/texture.py +453 -0
  71. sage/plot/plot3d/transform.cpython-314-x86_64-linux-gnu.so +0 -0
  72. sage/plot/plot3d/transform.pxd +21 -0
  73. sage/plot/plot3d/transform.pyx +268 -0
  74. sage/plot/plot3d/tri_plot.py +589 -0
  75. sage/plot/plot_field.py +362 -0
  76. sage/plot/point.py +624 -0
  77. sage/plot/polygon.py +562 -0
  78. sage/plot/primitive.py +249 -0
  79. sage/plot/scatter_plot.py +199 -0
  80. sage/plot/step.py +85 -0
  81. sage/plot/streamline_plot.py +328 -0
  82. sage/plot/text.py +432 -0
sage/plot/graphics.py ADDED
@@ -0,0 +1,3580 @@
1
+ # sage_setup: distribution = sagemath-plot
2
+ r"""
3
+ Graphics objects
4
+
5
+ This file contains the definition of the class :class:`Graphics`.
6
+ Usually, you don't call the constructor of this class directly
7
+ (although you can do it), you would use :func:`plot` instead.
8
+
9
+ AUTHORS:
10
+
11
+ - Jeroen Demeyer (2012-04-19): split off this file from plot.py (:issue:`12857`)
12
+
13
+ - Punarbasu Purkayastha (2012-05-20): Add logarithmic scale (:issue:`4529`)
14
+
15
+ - Emily Chen (2013-01-05): Add documentation for
16
+ :meth:`~sage.plot.graphics.Graphics.show` figsize parameter (:issue:`5956`)
17
+
18
+ - Eric Gourgoulhon (2015-03-19): Add parameter axes_labels_size (:issue:`18004`)
19
+
20
+ - Eric Gourgoulhon (2019-05-24): :class:`~sage.plot.multigraphics.GraphicsArray`
21
+ moved to new module :mod:`~sage.plot.multigraphics`; various improvements and
22
+ fixes in :meth:`Graphics.matplotlib` and ``Graphics._set_scale``; new method
23
+ :meth:`Graphics.inset`
24
+ """
25
+
26
+ # ****************************************************************************
27
+ # Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>
28
+ # Copyright (C) 2006-2008 William Stein <wstein@gmail.com>
29
+ # Copyright (C) 2010 Jason Grout
30
+ #
31
+ # Distributed under the terms of the GNU General Public License (GPL)
32
+ # as published by the Free Software Foundation; either version 2 of
33
+ # the License, or (at your option) any later version.
34
+ # https://www.gnu.org/licenses/
35
+ # ****************************************************************************
36
+
37
+ import os
38
+ from numbers import Integral
39
+ from collections.abc import Iterable
40
+ from math import isnan
41
+ import sage.misc.verbose
42
+ from sage.misc.temporary_file import tmp_filename
43
+ from sage.misc.fast_methods import WithEqualityById
44
+ from sage.structure.sage_object import SageObject
45
+ from sage.misc.decorators import suboptions
46
+ from .colors import rgbcolor
47
+
48
+ ALLOWED_EXTENSIONS = ['.eps', '.pdf', '.pgf', '.png', '.ps', '.sobj', '.svg']
49
+ DEFAULT_DPI = 100
50
+
51
+
52
+ # If do_verify is True, options are checked when drawing a
53
+ # GraphicsPrimitive. See primitive.py
54
+ do_verify = True
55
+
56
+
57
+ def is_Graphics(x):
58
+ """
59
+ Return ``True`` if `x` is a Graphics object.
60
+
61
+ EXAMPLES::
62
+
63
+ sage: from sage.plot.graphics import is_Graphics
64
+ sage: is_Graphics(1)
65
+ doctest:warning...
66
+ DeprecationWarning: The function is_Graphics is deprecated;
67
+ use 'isinstance(..., Graphics)' instead.
68
+ See https://github.com/sagemath/sage/issues/38184 for details.
69
+ False
70
+ sage: is_Graphics(disk((0.0, 0.0), 1, (0, pi/2))) # needs sage.symbolic
71
+ True
72
+ """
73
+ from sage.misc.superseded import deprecation
74
+ deprecation(38184,
75
+ "The function is_Graphics is deprecated; "
76
+ "use 'isinstance(..., Graphics)' instead.")
77
+ return isinstance(x, Graphics)
78
+
79
+
80
+ def _parse_figsize(figsize):
81
+ r"""
82
+ Helper function to get a figure size in matplotlib format.
83
+
84
+ INPUT:
85
+
86
+ - ``figsize`` -- width or [width, height] in inches; if only the width is
87
+ provided, the height is computed from matplotlib's default aspect ratio
88
+
89
+ OUTPUT:
90
+
91
+ - a pair of ``float``'s representing ``(width, height)``
92
+
93
+ EXAMPLES::
94
+
95
+ sage: from sage.plot.graphics import _parse_figsize
96
+ sage: _parse_figsize([5, 4])
97
+ (5.0, 4.0)
98
+
99
+ The default aspect ratio is 4/3::
100
+
101
+ sage: _parse_figsize(5) # tol 1.0e-13
102
+ (5.0, 3.75)
103
+ """
104
+ from matplotlib import rcParams
105
+ if isinstance(figsize, (list, tuple)):
106
+ # figsize should be a pair of positive numbers
107
+ if len(figsize) != 2:
108
+ raise ValueError("figsize should be a positive number or a list "
109
+ f"of two positive numbers, not {figsize}")
110
+ figsize = (float(figsize[0]), float(figsize[1])) # floats for mpl
111
+ if not (figsize[0] > 0 and figsize[1] > 0):
112
+ raise ValueError("figsize should be positive numbers, "
113
+ f"not {figsize[0]} and {figsize[1]}")
114
+ else:
115
+ # in this case, figsize is a single number representing the width and
116
+ # should be positive
117
+ try:
118
+ figsize = float(figsize) # to pass to mpl
119
+ except TypeError:
120
+ raise TypeError(f"figsize should be a positive number, not {figsize}")
121
+ if figsize > 0:
122
+ default_width, default_height = rcParams['figure.figsize']
123
+ figsize = (figsize, default_height * figsize / default_width)
124
+ else:
125
+ raise ValueError(f"figsize should be positive, not {figsize}")
126
+ return figsize
127
+
128
+
129
+ class Graphics(WithEqualityById, SageObject):
130
+ """
131
+ The Graphics object is an empty list of graphics objects. It is
132
+ useful to use this object when initializing a for loop where
133
+ different graphics object will be added to the empty object.
134
+
135
+ EXAMPLES::
136
+
137
+ sage: G = Graphics(); print(G)
138
+ Graphics object consisting of 0 graphics primitives
139
+ sage: c = circle((1,1), 1)
140
+ sage: G += c; print(G)
141
+ Graphics object consisting of 1 graphics primitive
142
+
143
+ Here we make a graphic of embedded isosceles triangles, coloring
144
+ each one with a different color as we go::
145
+
146
+ sage: h = 10; c = 0.4; p = 0.5
147
+ sage: G = Graphics()
148
+ sage: for x in srange(1, h+1): # needs sage.symbolic
149
+ ....: l = [[0,x*sqrt(3)],[-x/2,-x*sqrt(3)/2],[x/2,-x*sqrt(3)/2],[0,x*sqrt(3)]]
150
+ ....: G += line(l, color=hue(c + p*(x/h)))
151
+ sage: G.show(figsize=[5,5]) # needs sage.symbolic
152
+
153
+ We can change the scale of the axes in the graphics before displaying.::
154
+
155
+ sage: G = plot(exp, 1, 10) # long time # needs sage.symbolic
156
+ sage: G.show(scale='semilogy') # long time # needs sage.symbolic
157
+
158
+ TESTS:
159
+
160
+ From :issue:`4604`, ensure Graphics can handle 3d objects::
161
+
162
+ sage: g = Graphics()
163
+ sage: g += sphere((1, 1, 1), 2)
164
+ sage: g.show()
165
+
166
+ We check that graphics can be pickled (we can't use equality on
167
+ graphics so we just check that the load/dump cycle gives a
168
+ :class:`Graphics` instance)::
169
+
170
+ sage: g = Graphics()
171
+ sage: g2 = loads(dumps(g))
172
+ sage: g2.show()
173
+
174
+ ::
175
+
176
+ sage: isinstance(g2, Graphics)
177
+ True
178
+
179
+ sage: hash(Graphics()) # random
180
+ 42
181
+
182
+ .. automethod:: _rich_repr_
183
+ """
184
+
185
+ def __init__(self):
186
+ """
187
+ Create a new empty Graphics objects with all the defaults.
188
+
189
+ EXAMPLES::
190
+
191
+ sage: G = Graphics()
192
+ """
193
+ self._axes_color = (0, 0, 0)
194
+ self._axes_label_color = (0, 0, 0)
195
+ self._axes_width = 0.8
196
+ self._bbox_extra_artists = []
197
+ self._extra_kwds = {}
198
+ self._fontsize = 10
199
+ self._axes_labels_size = 1.6
200
+ self._legend_colors = []
201
+ self._legend_opts = {}
202
+ self._objects = []
203
+ self._show_axes = True
204
+ self._show_legend = False
205
+ self._tick_label_color = (0, 0, 0)
206
+
207
+ def set_aspect_ratio(self, ratio):
208
+ """
209
+ Set the aspect ratio, which is the ratio of height and width
210
+ of a unit square (i.e., height/width of a unit square), or
211
+ 'automatic' (expand to fill the figure).
212
+
213
+ INPUT:
214
+
215
+ - ``ratio`` -- a positive real number or 'automatic'
216
+
217
+ EXAMPLES: We create a plot of the upper half of a circle, but it
218
+ doesn't look round because the aspect ratio is off::
219
+
220
+ sage: P = plot(sqrt(1-x^2),(x,-1,1)); P # needs sage.symbolic
221
+ Graphics object consisting of 1 graphics primitive
222
+
223
+ So we set the aspect ratio and now it is round::
224
+
225
+ sage: P.set_aspect_ratio(1) # needs sage.symbolic
226
+ sage: P.aspect_ratio() # needs sage.symbolic
227
+ 1.0
228
+ sage: P # needs sage.symbolic
229
+ Graphics object consisting of 1 graphics primitive
230
+
231
+ Note that the aspect ratio is inherited upon addition (which takes
232
+ the max of aspect ratios of objects whose aspect ratio has been
233
+ set)::
234
+
235
+ sage: P + plot(sqrt(4-x^2),(x,-2,2)) # needs sage.symbolic
236
+ Graphics object consisting of 2 graphics primitives
237
+
238
+ In the following example, both plots produce a circle that looks
239
+ twice as tall as wide::
240
+
241
+ sage: Q = circle((0,0), 0.5); Q.set_aspect_ratio(2)
242
+ sage: (P + Q).aspect_ratio(); P + Q # needs sage.symbolic
243
+ 2.0
244
+ Graphics object consisting of 2 graphics primitives
245
+ sage: (Q + P).aspect_ratio(); Q + P # needs sage.symbolic
246
+ 2.0
247
+ Graphics object consisting of 2 graphics primitives
248
+ """
249
+ if ratio != 'auto' and ratio != 'automatic':
250
+ ratio = float(ratio)
251
+ if ratio <= 0:
252
+ raise ValueError("the aspect ratio must be positive or 'automatic'")
253
+ else:
254
+ ratio = 'automatic'
255
+ self._extra_kwds['aspect_ratio'] = ratio
256
+
257
+ def aspect_ratio(self):
258
+ """
259
+ Get the current aspect ratio, which is the ratio of height to
260
+ width of a unit square, or ``'automatic'``.
261
+
262
+ OUTPUT: a positive float (height/width of a unit square), or ``'automatic'``
263
+ (expand to fill the figure).
264
+
265
+ EXAMPLES:
266
+
267
+ The default aspect ratio for a new blank :class:`Graphics` object is ``'automatic'``::
268
+
269
+ sage: P = Graphics()
270
+ sage: P.aspect_ratio()
271
+ 'automatic'
272
+
273
+ The aspect ratio can be explicitly set different from the object's default::
274
+
275
+ sage: P = circle((1,1), 1)
276
+ sage: P.aspect_ratio()
277
+ 1.0
278
+ sage: P.set_aspect_ratio(2)
279
+ sage: P.aspect_ratio()
280
+ 2.0
281
+ sage: P.set_aspect_ratio('automatic')
282
+ sage: P.aspect_ratio()
283
+ 'automatic'
284
+ """
285
+ return self._extra_kwds.get('aspect_ratio', 'automatic')
286
+
287
+ def legend(self, show=None):
288
+ r"""
289
+ Set whether or not the legend is shown by default.
290
+
291
+ INPUT:
292
+
293
+ - ``show`` -- (default: ``None``) a boolean
294
+
295
+ If called with no input, return the current legend setting.
296
+
297
+ EXAMPLES:
298
+
299
+ By default no legend is displayed::
300
+
301
+ sage: P = plot(sin) # needs sage.symbolic
302
+ sage: P.legend() # needs sage.symbolic
303
+ False
304
+
305
+ But if we put a label then the legend is shown::
306
+
307
+ sage: P = plot(sin, legend_label='sin') # needs sage.symbolic
308
+ sage: P.legend() # needs sage.symbolic
309
+ True
310
+
311
+ We can turn it on or off::
312
+
313
+ sage: # needs sage.symbolic
314
+ sage: P.legend(False)
315
+ sage: P.legend()
316
+ False
317
+ sage: P.legend(True)
318
+ sage: P # show with the legend
319
+ Graphics object consisting of 1 graphics primitive
320
+ """
321
+ if show is None:
322
+ return self._show_legend
323
+ else:
324
+ self._show_legend = bool(show)
325
+
326
+ def set_legend_options(self, **kwds):
327
+ r"""
328
+ Set various legend options.
329
+
330
+ INPUT:
331
+
332
+ - ``title`` -- (default: ``None``) string, the legend title
333
+
334
+ - ``ncol`` -- (default: 1) positive integer, the number of columns
335
+
336
+ - ``columnspacing`` -- (default: ``None``) the spacing between columns
337
+
338
+ - ``borderaxespad`` -- (default: ``None``) float, length between the axes and the legend
339
+
340
+ - ``back_color`` -- (default: ``'white'``) this parameter can be a string
341
+ denoting a color or an RGB tuple. The string can be a color name
342
+ as in ('red', 'green', 'yellow', ...) or a floating point number
343
+ like '0.8' which gets expanded to (0.8, 0.8, 0.8). The
344
+ tuple form is just a floating point RGB tuple with all values ranging
345
+ from 0 to 1.
346
+
347
+ - ``handlelength`` -- (default: 0.05) float, the length of the legend handles
348
+
349
+ - ``handletextpad`` -- (default: 0.5) float, the pad between the legend handle and text
350
+
351
+ - ``labelspacing`` -- (default: 0.02) float, vertical space between legend entries
352
+
353
+ - ``loc`` -- (default: ``'best'``) may be a string, an integer or a tuple. String or
354
+ integer inputs must be one of the following:
355
+
356
+ - 0, 'best'
357
+
358
+ - 1, 'upper right'
359
+
360
+ - 2, 'upper left'
361
+
362
+ - 3, 'lower left'
363
+
364
+ - 4, 'lower right'
365
+
366
+ - 5, 'right'
367
+
368
+ - 6, 'center left'
369
+
370
+ - 7, 'center right'
371
+
372
+ - 8, 'lower center'
373
+
374
+ - 9, 'upper center'
375
+
376
+ - 10, 'center'
377
+
378
+ - Tuple arguments represent an absolute (x, y) position on the plot
379
+ in axes coordinates (meaning from 0 to 1 in each direction).
380
+
381
+ - ``markerscale`` -- (default: 0.6) float, how much to scale the markers in the legend
382
+
383
+ - ``numpoints`` -- (default: 2) integer, the number of points in the legend for line
384
+
385
+ - ``borderpad`` -- (default: 0.6) float, the fractional whitespace inside the legend border
386
+ (between 0 and 1)
387
+
388
+ - ``font_family`` -- (default: ``'sans-serif'``) string, one of
389
+ ``'serif'``, ``'sans-serif'``, ``'cursive'``, ``'fantasy'``,
390
+ ``'monospace'``
391
+
392
+ - ``font_style`` -- (default: ``'normal'``) string, one of
393
+ ``'normal'``, ``'italic'``, ``'oblique'``
394
+
395
+ - ``font_variant`` -- (default: ``'normal'``) string, one of
396
+ ``'normal'``, ``'small-caps'``
397
+
398
+ - ``font_weight`` -- (default: ``'medium'``) string, one of
399
+ ``'black'``, ``'extra bold'``, ``'bold'``, ``'semibold'``,
400
+ ``'medium'``, ``'normal'``, ``'light'``
401
+
402
+ - ``font_size`` -- (default: ``'medium'``) string, one of
403
+ ``'xx-small'``, ``'x-small'``, ``'small'``, ``'medium'``,
404
+ ``'large'``, ``'x-large'``, ``'xx-large'``, or an absolute font size
405
+ (e.g. 12)
406
+
407
+ - ``shadow`` -- boolean (default: ``True``); draw a shadow behind the legend
408
+
409
+ - ``fancybox`` -- boolean (default: ``False``); if
410
+ ``True``, draws a frame with a round fancybox
411
+
412
+ These are all keyword arguments.
413
+
414
+ OUTPUT: a dictionary of all current legend options
415
+
416
+ EXAMPLES:
417
+
418
+ By default, no options are set::
419
+
420
+ sage: p = plot(tan, legend_label='tan') # needs sage.symbolic
421
+ sage: p.set_legend_options() # needs sage.symbolic
422
+ {}
423
+
424
+ We build a legend without a shadow::
425
+
426
+ sage: p.set_legend_options(shadow=False) # needs sage.symbolic
427
+ sage: p.set_legend_options()['shadow'] # needs sage.symbolic
428
+ False
429
+
430
+ To set the legend position to the center of the plot, all these
431
+ methods are roughly equivalent::
432
+
433
+ sage: p.set_legend_options(loc='center'); p # needs sage.symbolic
434
+ Graphics object consisting of 1 graphics primitive
435
+
436
+ ::
437
+
438
+ sage: p.set_legend_options(loc=10); p # needs sage.symbolic
439
+ Graphics object consisting of 1 graphics primitive
440
+
441
+ ::
442
+
443
+ sage: p.set_legend_options(loc=(0.5,0.5)); p # aligns the bottom of the box to the center # needs sage.symbolic
444
+ Graphics object consisting of 1 graphics primitive
445
+
446
+ The parameters ``loc`` and ``borderaxespad`` can be altered
447
+ in order to place the legend below the x-axis label or to
448
+ the left of the y-axis label::
449
+
450
+ sage: p = line([(0, 0), (1, 1)], legend_label='test')
451
+ sage: p.axes_labels(['X-Label', 'Y-Label']) # adding labels for axes
452
+ sage: p.set_legend_options(loc=8, borderaxespad=-7.5-0.01*p.fontsize())
453
+ sage: p
454
+ Graphics object consisting of 1 graphics primitive
455
+ """
456
+ if len(kwds) == 0:
457
+ return self._legend_opts
458
+ else:
459
+ self._legend_opts.update(kwds)
460
+
461
+ def get_axes_range(self):
462
+ """
463
+ Return a dictionary of the range of the axes for this graphics
464
+ object. This is fall back to the ranges in ``get_minmax_data()`` for
465
+ any value which the user has not explicitly set.
466
+
467
+ .. warning::
468
+
469
+ Changing the dictionary returned by this function does not
470
+ change the axes range for this object. To do that, use the
471
+ :meth:`set_axes_range` method.
472
+
473
+ EXAMPLES::
474
+
475
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
476
+ sage: list(sorted(L.get_axes_range().items()))
477
+ [('xmax', 3.0), ('xmin', 1.0), ('ymax', 5.0), ('ymin', -4.0)]
478
+ sage: L.set_axes_range(xmin=-1)
479
+ sage: list(sorted(L.get_axes_range().items()))
480
+ [('xmax', 3.0), ('xmin', -1.0), ('ymax', 5.0), ('ymin', -4.0)]
481
+ """
482
+ axes_range = self.get_minmax_data()
483
+ axes_range.update(self._get_axes_range_dict())
484
+ return axes_range
485
+
486
+ def set_axes_range(self, xmin=None, xmax=None, ymin=None, ymax=None):
487
+ """
488
+ Set the ranges of the `x` and `y` axes.
489
+
490
+ INPUT:
491
+
492
+ - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- floats
493
+
494
+ EXAMPLES::
495
+
496
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
497
+ sage: L.set_axes_range(-1, 20, 0, 2)
498
+ sage: d = L.get_axes_range()
499
+ sage: d['xmin'], d['xmax'], d['ymin'], d['ymax']
500
+ (-1.0, 20.0, 0.0, 2.0)
501
+ """
502
+ l = locals()
503
+ axes_range = self._get_axes_range_dict()
504
+ for name in ['xmin', 'xmax', 'ymin', 'ymax']:
505
+ if l[name] is not None:
506
+ axes_range[name] = float(l[name])
507
+
508
+ axes_range = set_axes_range
509
+
510
+ def _get_axes_range_dict(self):
511
+ """
512
+ Return the underlying dictionary used to store the user's
513
+ custom ranges for the axes on this object.
514
+
515
+ EXAMPLES::
516
+
517
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
518
+ sage: L._get_axes_range_dict()
519
+ {}
520
+ sage: L.set_axes_range(xmin=-1)
521
+ sage: L._get_axes_range_dict()
522
+ {'xmin': -1.0}
523
+ """
524
+ try:
525
+ return self._axes_range
526
+ except AttributeError:
527
+ self._axes_range = {}
528
+ return self._axes_range
529
+
530
+ def set_flip(self, flip_x=None, flip_y=None):
531
+ """
532
+ Set the flip options for this graphics object.
533
+
534
+ INPUT:
535
+
536
+ - ``flip_x`` -- boolean (default: ``None``); if not ``None``, set the
537
+ ``flip_x`` option to this value
538
+ - ``flip_y`` -- boolean (default: ``None``); if not ``None``, set the
539
+ ``flip_y`` option to this value
540
+
541
+ EXAMPLES::
542
+
543
+ sage: L = line([(1, 0), (2, 3)])
544
+ sage: L.set_flip(flip_y=True)
545
+ sage: L.flip()
546
+ (False, True)
547
+ sage: L.set_flip(True, False)
548
+ sage: L.flip()
549
+ (True, False)
550
+ """
551
+ if flip_x is not None:
552
+ self._extra_kwds['flip_x'] = flip_x
553
+ if flip_y is not None:
554
+ self._extra_kwds['flip_y'] = flip_y
555
+
556
+ def flip(self, flip_x=False, flip_y=False):
557
+ """
558
+ Get the flip options and optionally mirror this graphics object.
559
+
560
+ INPUT:
561
+
562
+ - ``flip_x`` -- boolean (default: ``False``); if ``True``, replace the
563
+ current ``flip_x`` option by its opposite
564
+ - ``flip_y`` -- boolean (default: ``False``); if ``True``, replace the
565
+ current ``flip_y`` option by its opposite
566
+
567
+ OUTPUT: a tuple containing the new flip options
568
+
569
+ EXAMPLES:
570
+
571
+ When called without arguments, this just returns the current flip
572
+ options::
573
+
574
+ sage: L = line([(1, 0), (2, 3)])
575
+ sage: L.flip()
576
+ (False, False)
577
+
578
+ Otherwise, the specified options are changed and the new options are
579
+ returned::
580
+
581
+ sage: L.flip(flip_y=True)
582
+ (False, True)
583
+ sage: L.flip(True, True)
584
+ (True, False)
585
+ """
586
+ a = self._extra_kwds.get('flip_x', self.SHOW_OPTIONS['flip_x'])
587
+ b = self._extra_kwds.get('flip_y', self.SHOW_OPTIONS['flip_y'])
588
+ if flip_x:
589
+ a = not a
590
+ self._extra_kwds['flip_x'] = a
591
+ if flip_y:
592
+ b = not b
593
+ self._extra_kwds['flip_y'] = b
594
+ return (a, b)
595
+
596
+ def fontsize(self, s=None):
597
+ """
598
+ Set the font size of axes labels and tick marks.
599
+
600
+ Note that the relative size of the axes labels font w.r.t. the tick
601
+ marks font can be adjusted via :meth:`axes_labels_size`.
602
+
603
+ INPUT:
604
+
605
+ - ``s`` -- integer, a font size in points
606
+
607
+
608
+ If called with no input, return the current fontsize.
609
+
610
+ EXAMPLES::
611
+
612
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
613
+ sage: L.fontsize()
614
+ 10
615
+ sage: L.fontsize(20)
616
+ sage: L.fontsize()
617
+ 20
618
+
619
+ All the numbers on the axes will be very large in this plot::
620
+
621
+ sage: L
622
+ Graphics object consisting of 1 graphics primitive
623
+ """
624
+ if s is None:
625
+ try:
626
+ return self._fontsize
627
+ except AttributeError:
628
+ self._fontsize = 10
629
+ return self._fontsize
630
+ self._fontsize = int(s)
631
+
632
+ def axes_labels_size(self, s=None):
633
+ """
634
+ Set the relative size of axes labels w.r.t. the axes tick marks.
635
+
636
+ INPUT:
637
+
638
+ - ``s`` -- float, relative size of axes labels w.r.t. to the tick marks,
639
+ the size of the tick marks being set by :meth:`fontsize`
640
+
641
+ If called with no input, return the current relative size.
642
+
643
+ EXAMPLES::
644
+
645
+ sage: # needs sage.symbolic
646
+ sage: p = plot(sin(x^2), (x, -3, 3), axes_labels=['$x$','$y$'])
647
+ sage: p.axes_labels_size() # default value
648
+ 1.6
649
+ sage: p.axes_labels_size(2.5)
650
+ sage: p.axes_labels_size()
651
+ 2.5
652
+
653
+ Now the axes labels are large w.r.t. the tick marks::
654
+
655
+ sage: p # needs sage.symbolic
656
+ Graphics object consisting of 1 graphics primitive
657
+ """
658
+ if s is None:
659
+ try:
660
+ return self._axes_labels_size
661
+ except AttributeError:
662
+ self._axes_labels_size = 1.6
663
+ return self._axes_labels_size
664
+ self._axes_labels_size = float(s)
665
+
666
+ def axes(self, show=None):
667
+ """
668
+ Set whether or not the `x` and `y` axes are shown
669
+ by default.
670
+
671
+ INPUT:
672
+
673
+ - ``show`` -- boolean
674
+
675
+ If called with no input, return the current axes setting.
676
+
677
+ EXAMPLES::
678
+
679
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
680
+
681
+ By default the axes are displayed.
682
+
683
+ ::
684
+
685
+ sage: L.axes()
686
+ True
687
+
688
+ But we turn them off, and verify that they are off
689
+
690
+ ::
691
+
692
+ sage: L.axes(False)
693
+ sage: L.axes()
694
+ False
695
+
696
+ Displaying L now shows a triangle but no axes.
697
+
698
+ ::
699
+
700
+ sage: L
701
+ Graphics object consisting of 1 graphics primitive
702
+ """
703
+ if show is None:
704
+ try:
705
+ return self._show_axes
706
+ except AttributeError:
707
+ self._show_axes = True
708
+ return self._show_axes
709
+ self._show_axes = bool(show)
710
+
711
+ def axes_color(self, c=None):
712
+ """
713
+ Set the axes color.
714
+
715
+ If called with no input, return the current axes_color setting.
716
+
717
+ INPUT:
718
+
719
+ - ``c`` -- an RGB color 3-tuple, where each tuple entry
720
+ is a float between 0 and 1
721
+
722
+ EXAMPLES: We create a line, which has like everything a default
723
+ axes color of black.
724
+
725
+ ::
726
+
727
+ sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
728
+ sage: L.axes_color()
729
+ (0, 0, 0)
730
+
731
+ We change the axes color to red and verify the change.
732
+
733
+ ::
734
+
735
+ sage: L.axes_color((1,0,0))
736
+ sage: L.axes_color()
737
+ (1.0, 0.0, 0.0)
738
+
739
+ When we display the plot, we'll see a blue triangle and bright red
740
+ axes.
741
+
742
+ ::
743
+
744
+ sage: L
745
+ Graphics object consisting of 1 graphics primitive
746
+ """
747
+ if c is None:
748
+ try:
749
+ return self._axes_color
750
+
751
+ except AttributeError:
752
+ self._axes_color = (0.0, 0.0, 0.0)
753
+ return self._axes_color
754
+ self._axes_color = rgbcolor(c)
755
+
756
+ def axes_labels(self, l=None):
757
+ """
758
+ Set the axes labels.
759
+
760
+ INPUT:
761
+
762
+ - ``l`` -- (default: ``None``) a list of two strings or
763
+ ``None``
764
+
765
+ OUTPUT: a 2-tuple of strings
766
+
767
+ If l is ``None``, returns the current ``axes_labels``,
768
+ which is itself by default ``None``. The default labels are both
769
+ empty.
770
+
771
+ EXAMPLES: We create a plot and put x and y axes labels on it.
772
+
773
+ ::
774
+
775
+ sage: p = plot(sin(x), (x, 0, 10)) # needs sage.symbolic
776
+ sage: p.axes_labels(['$x$','$y$']) # needs sage.symbolic
777
+ sage: p.axes_labels() # needs sage.symbolic
778
+ ('$x$', '$y$')
779
+
780
+ Now when you plot p, you see x and y axes labels::
781
+
782
+ sage: p # needs sage.symbolic
783
+ Graphics object consisting of 1 graphics primitive
784
+
785
+ Notice that some may prefer axes labels which are not
786
+ typeset::
787
+
788
+ sage: plot(sin(x), (x, 0, 10), axes_labels=['x','y']) # needs sage.symbolic
789
+ Graphics object consisting of 1 graphics primitive
790
+
791
+ TESTS:
792
+
793
+ Unicode strings are acceptable; see :issue:`13161`. Note that
794
+ this does not guarantee that matplotlib will handle the strings
795
+ properly, although it should.
796
+
797
+ ::
798
+
799
+ sage: c = circle((0,0), 1)
800
+ sage: c.axes_labels(['axe des abscisses', 'axe des ordonnées'])
801
+ sage: c._axes_labels
802
+ ('axe des abscisses', 'axe des ordonnées')
803
+ """
804
+ if l is None:
805
+ try:
806
+ return self._axes_labels
807
+ except AttributeError:
808
+ self._axes_labels = None
809
+ return self._axes_labels
810
+ if not isinstance(l, (list, tuple)):
811
+ raise TypeError("l must be a list or tuple")
812
+ if len(l) != 2:
813
+ raise ValueError("l must have length 2")
814
+ self._axes_labels = tuple(l)
815
+
816
+ def axes_label_color(self, c=None):
817
+ r"""
818
+ Set the color of the axes labels.
819
+
820
+ The axes labels are placed at the edge of the x and y axes, and are
821
+ not on by default (use the :meth:`axes_labels` command to
822
+ set them; see the example below). This function just changes their
823
+ color.
824
+
825
+ INPUT:
826
+
827
+ - ``c`` -- an RGB 3-tuple of numbers between 0 and 1
828
+
829
+
830
+ If called with no input, return the current axes_label_color
831
+ setting.
832
+
833
+ EXAMPLES: We create a plot, which by default has axes label color
834
+ black.
835
+
836
+ ::
837
+
838
+ sage: p = plot(sin, (-1,1)) # needs sage.symbolic
839
+ sage: p.axes_label_color() # needs sage.symbolic
840
+ (0, 0, 0)
841
+
842
+ We change the labels to be red, and confirm this::
843
+
844
+ sage: p.axes_label_color((1,0,0)) # needs sage.symbolic
845
+ sage: p.axes_label_color() # needs sage.symbolic
846
+ (1.0, 0.0, 0.0)
847
+
848
+ We set labels, since otherwise we won't see anything.
849
+
850
+ ::
851
+
852
+ sage: p.axes_labels(['$x$ axis', '$y$ axis']) # needs sage.symbolic
853
+
854
+ In the plot below, notice that the labels are red::
855
+
856
+ sage: p # needs sage.symbolic
857
+ Graphics object consisting of 1 graphics primitive
858
+ """
859
+ if c is None:
860
+ try:
861
+ return self._axes_label_color
862
+ except AttributeError:
863
+ self._axes_label_color = (0, 0, 0)
864
+ return self._axes_label_color
865
+ self._axes_label_color = rgbcolor(c)
866
+
867
+ def axes_width(self, w=None):
868
+ r"""
869
+ Set the axes width. Use this to draw a plot with really fat or
870
+ really thin axes.
871
+
872
+ INPUT:
873
+
874
+ - ``w`` -- a float
875
+
876
+
877
+ If called with no input, return the current
878
+ ``axes_width`` setting.
879
+
880
+ EXAMPLES: We create a plot, see the default axes width (with funny
881
+ Python float rounding), then reset the width to 10 (very fat).
882
+
883
+ ::
884
+
885
+ sage: # needs sage.symbolic
886
+ sage: p = plot(cos, (-3,3))
887
+ sage: p.axes_width()
888
+ 0.8
889
+ sage: p.axes_width(10)
890
+ sage: p.axes_width()
891
+ 10.0
892
+
893
+ Finally we plot the result, which is a graph with very fat axes.
894
+
895
+ ::
896
+
897
+ sage: p # needs sage.symbolic
898
+ Graphics object consisting of 1 graphics primitive
899
+ """
900
+ if w is None:
901
+ try:
902
+ return self._axes_width
903
+ except AttributeError:
904
+ self._axes_width = True
905
+ return self._axes_width
906
+ self._axes_width = float(w)
907
+
908
+ def tick_label_color(self, c=None):
909
+ """
910
+ Set the color of the axes tick labels.
911
+
912
+ INPUT:
913
+
914
+ - ``c`` -- an RGB 3-tuple of numbers between 0 and 1
915
+
916
+
917
+ If called with no input, return the current ``tick_label_color``
918
+ setting.
919
+
920
+ EXAMPLES::
921
+
922
+ sage: # needs sage.symbolic
923
+ sage: p = plot(cos, (-3,3))
924
+ sage: p.tick_label_color()
925
+ (0, 0, 0)
926
+ sage: p.tick_label_color((1,0,0))
927
+ sage: p.tick_label_color()
928
+ (1.0, 0.0, 0.0)
929
+ sage: p
930
+ Graphics object consisting of 1 graphics primitive
931
+ """
932
+ if c is None:
933
+ try:
934
+ return self._tick_label_color
935
+ except AttributeError:
936
+ self._tick_label_color = (0, 0, 0)
937
+ return self._tick_label_color
938
+ self._tick_label_color = rgbcolor(c)
939
+
940
+ def _repr_(self):
941
+ r"""
942
+ Return a string representation of the graphics objects.
943
+
944
+ OUTPUT: string
945
+
946
+ EXAMPLES:
947
+
948
+ We create a plot and call :meth:`show` on it, which causes it
949
+ to be displayed as a plot::
950
+
951
+ sage: P = plot(cos, (-1,1)) # needs sage.symbolic
952
+ sage: P.show() # needs sage.symbolic
953
+
954
+ Just doing this also displays the plot::
955
+
956
+ sage: P # needs sage.symbolic
957
+ Graphics object consisting of 1 graphics primitive
958
+
959
+ Using the Python `repr` or `str` commands do not display the
960
+ plot::
961
+
962
+ sage: repr(P) # needs sage.symbolic
963
+ 'Graphics object consisting of 1 graphics primitive'
964
+ sage: str(P) # needs sage.symbolic
965
+ 'Graphics object consisting of 1 graphics primitive'
966
+ sage: print(P) # needs sage.symbolic
967
+ Graphics object consisting of 1 graphics primitive
968
+
969
+ TESTS::
970
+
971
+ sage: P._repr_() # needs sage.symbolic
972
+ 'Graphics object consisting of 1 graphics primitive'
973
+ """
974
+ return str(self)
975
+
976
+ def _rich_repr_(self, display_manager, **kwds):
977
+ """
978
+ Rich Output Magic Method.
979
+
980
+ See :mod:`sage.repl.rich_output` for details.
981
+
982
+ EXAMPLES::
983
+
984
+ sage: from sage.repl.rich_output import get_display_manager
985
+ sage: dm = get_display_manager()
986
+ sage: g = Graphics()
987
+ sage: g._rich_repr_(dm)
988
+ OutputImagePng container
989
+ """
990
+ types = display_manager.types
991
+ prefer_raster = (
992
+ ('.png', types.OutputImagePng),
993
+ ('.jpg', types.OutputImageJpg),
994
+ ('.gif', types.OutputImageGif),
995
+ )
996
+ prefer_vector = (
997
+ ('.svg', types.OutputImageSvg),
998
+ ('.pdf', types.OutputImagePdf),
999
+ )
1000
+ graphics = display_manager.preferences.graphics
1001
+ if graphics == 'disable':
1002
+ return
1003
+ elif graphics == 'raster' or graphics is None:
1004
+ preferred = prefer_raster + prefer_vector
1005
+ elif graphics == 'vector':
1006
+ preferred = prefer_vector + prefer_raster
1007
+ else:
1008
+ raise ValueError('unknown graphics output preference')
1009
+ for file_ext, output_container in preferred:
1010
+ if output_container in display_manager.supported_output():
1011
+ return display_manager.graphics_from_save(
1012
+ self.save, kwds, file_ext, output_container)
1013
+
1014
+ def __str__(self):
1015
+ r"""
1016
+ Return string representation of this plot.
1017
+
1018
+ OUTPUT: string
1019
+
1020
+ EXAMPLES::
1021
+
1022
+ sage: S = circle((0,0), 2); S.__str__()
1023
+ 'Graphics object consisting of 1 graphics primitive'
1024
+ sage: str(S)
1025
+ 'Graphics object consisting of 1 graphics primitive'
1026
+ sage: print(S)
1027
+ Graphics object consisting of 1 graphics primitive
1028
+ """
1029
+ s = "Graphics object consisting of %s graphics primitives" % (len(self))
1030
+ if len(self) == 1:
1031
+ s = s[:-1]
1032
+ return s
1033
+
1034
+ def __getitem__(self, i):
1035
+ """
1036
+ Return the i-th graphics primitive object:
1037
+
1038
+ EXAMPLES::
1039
+
1040
+ sage: G = circle((1,1),2) + circle((2,2),5); print(G)
1041
+ Graphics object consisting of 2 graphics primitives
1042
+ sage: G[1]
1043
+ Circle defined by (2.0,2.0) with r=5.0
1044
+ """
1045
+ return self._objects[i]
1046
+
1047
+ def __len__(self):
1048
+ """
1049
+ If G is of type Graphics, then len(G) gives the number of distinct
1050
+ graphics primitives making up that object.
1051
+
1052
+ EXAMPLES::
1053
+
1054
+ sage: G = circle((1,1),1) + circle((1,2),1) + circle((1,2),5); print(G)
1055
+ Graphics object consisting of 3 graphics primitives
1056
+ sage: len(G)
1057
+ 3
1058
+ """
1059
+ return len(self._objects)
1060
+
1061
+ def __delitem__(self, i):
1062
+ """
1063
+ If G is of type Graphics, then del(G[i]) removes the i-th distinct
1064
+ graphic primitive making up that object.
1065
+
1066
+ EXAMPLES::
1067
+
1068
+ sage: G = circle((1,1),1) + circle((1,2),1) + circle((1,2),5); print(G)
1069
+ Graphics object consisting of 3 graphics primitives
1070
+ sage: len(G)
1071
+ 3
1072
+ sage: del(G[2])
1073
+ sage: print(G)
1074
+ Graphics object consisting of 2 graphics primitives
1075
+ sage: len(G)
1076
+ 2
1077
+ """
1078
+ del self._objects[int(i)]
1079
+
1080
+ def __setitem__(self, i, x):
1081
+ """
1082
+ You can replace a :class:`GraphicPrimitive` (point, line, circle, etc...) in
1083
+ a :class:`Graphics` object G with any other :class:`GraphicPrimitive`
1084
+
1085
+ EXAMPLES::
1086
+
1087
+ sage: G = circle((1,1),1) + circle((1,2),1) + circle((1,2),5); print(G)
1088
+ Graphics object consisting of 3 graphics primitives
1089
+
1090
+ ::
1091
+
1092
+ sage: p = polygon([[1,3],[2,-2],[1,1],[1,3]]); print(p)
1093
+ Graphics object consisting of 1 graphics primitive
1094
+
1095
+ ::
1096
+
1097
+ sage: G[1] = p[0]
1098
+ sage: G # show the plot
1099
+ Graphics object consisting of 3 graphics primitives
1100
+ """
1101
+ from sage.plot.primitive import GraphicPrimitive
1102
+ if not isinstance(x, GraphicPrimitive):
1103
+ raise TypeError("x must be a GraphicPrimitive")
1104
+ self._objects[int(i)] = x
1105
+
1106
+ def __radd__(self, other):
1107
+ """
1108
+ Compute and return other + this graphics object.
1109
+
1110
+ This only works when other is a Python int equal to 0. In all other
1111
+ cases a :exc:`TypeError` is raised. The main reason for this
1112
+ function is to make summing a list of graphics objects easier.
1113
+
1114
+ EXAMPLES::
1115
+
1116
+ sage: S = circle((0,0), 2)
1117
+ sage: print(int(0) + S)
1118
+ Graphics object consisting of 1 graphics primitive
1119
+ sage: print(S + int(0))
1120
+ Graphics object consisting of 1 graphics primitive
1121
+
1122
+ The following would fail were it not for this function::
1123
+
1124
+ sage: v = [circle((0,0), 2), circle((2,3), 1)]
1125
+ sage: print(sum(v))
1126
+ Graphics object consisting of 2 graphics primitives
1127
+ """
1128
+ if isinstance(other, int) and other == 0:
1129
+ return self
1130
+ raise TypeError
1131
+
1132
+ def __add__(self, other):
1133
+ """
1134
+ If you have any Graphics object G1, you can always add any other
1135
+ amount of Graphics objects G2,G3,... to form a new Graphics object:
1136
+ ``G4 = G1 + G2 + G3``.
1137
+
1138
+ The xmin, xmax, ymin, and ymax properties of the graphics objects
1139
+ are expanded to include all objects in both scenes. If the aspect
1140
+ ratio property of either or both objects are set, then the larger
1141
+ aspect ratio is chosen, with 'automatic' being overridden by a
1142
+ numeric aspect ratio.
1143
+
1144
+ If one of the graphics object is set to show a legend, then
1145
+ the resulting object will also be set to show a legend. Legend
1146
+ options are propagated if set. If the same legend option is
1147
+ present in both arguments, the latter value is used.
1148
+
1149
+ EXAMPLES::
1150
+
1151
+ sage: g1 = plot(abs(sqrt(x^3-1)), (x,1,5), frame=True) # needs sage.symbolic
1152
+ sage: g2 = plot(-abs(sqrt(x^3-1)), (x,1,5), color='red') # needs sage.symbolic
1153
+ sage: g1 + g2 # displays the plot # needs sage.symbolic
1154
+ Graphics object consisting of 2 graphics primitives
1155
+
1156
+ TESTS:
1157
+
1158
+ Extra keywords to show are propagated::
1159
+
1160
+ sage: # needs sage.symbolic
1161
+ sage: (g1 + g2)._extra_kwds=={'aspect_ratio': 'automatic', 'frame': True}
1162
+ True
1163
+ sage: g1.set_aspect_ratio(2)
1164
+ sage: (g1+g2).aspect_ratio()
1165
+ 2.0
1166
+ sage: g2.set_aspect_ratio(3)
1167
+ sage: (g1+g2).aspect_ratio()
1168
+ 3.0
1169
+
1170
+ As are legend options, :issue:`12936`::
1171
+
1172
+ sage: # needs sage.symbolic
1173
+ sage: p1 = plot(x, x, 0, 1)
1174
+ sage: p2 = p1
1175
+ sage: p1.set_legend_options(back_color='black')
1176
+ sage: p2.set_legend_options(shadow=False)
1177
+ sage: p3 = p1 + p2
1178
+ sage: p3._legend_opts
1179
+ {'back_color': 'black', 'shadow': False}
1180
+
1181
+ If the same legend option is specified more than once, the
1182
+ latter takes precedence::
1183
+
1184
+ sage: # needs sage.symbolic
1185
+ sage: p1 = plot(x, x, 0, 1)
1186
+ sage: p2 = p1
1187
+ sage: p1.set_legend_options(shadow=True)
1188
+ sage: p2.set_legend_options(shadow=False)
1189
+ sage: p3 = p1 + p2
1190
+ sage: p3._legend_opts
1191
+ {'shadow': False}
1192
+
1193
+ Flipped axes take precedence over non-flipped axes::
1194
+
1195
+ sage: p1 = plot(x, x, 0, 1, flip_x=True, flip_y=True) # needs sage.symbolic
1196
+ sage: p2 = plot(x^2, x, 0, 1) # needs sage.symbolic
1197
+ sage: [p._extra_kwds[k] for p in [p1 + p2, p2 + p1] # needs sage.symbolic
1198
+ ....: for k in ['flip_x', 'flip_y']]
1199
+ [True, True, True, True]
1200
+ """
1201
+ if isinstance(other, int) and other == 0:
1202
+ return self
1203
+ if not isinstance(other, Graphics):
1204
+ from sage.plot.plot3d.base import Graphics3d
1205
+ if isinstance(other, Graphics3d):
1206
+ return self.plot3d() + other
1207
+ raise TypeError("other (=%s) must be a Graphics objects" % other)
1208
+ g = Graphics()
1209
+ g._objects = self._objects + other._objects
1210
+ g._show_legend = self._show_legend or other._show_legend
1211
+ g._extra_kwds.update(self._extra_kwds)
1212
+ g._extra_kwds.update(other._extra_kwds)
1213
+ g._legend_colors = self._legend_colors + other._legend_colors
1214
+ g._legend_opts.update(self._legend_opts)
1215
+ g._legend_opts.update(other._legend_opts)
1216
+ if 'flip_x' in self._extra_kwds and 'flip_x' in other._extra_kwds:
1217
+ g._extra_kwds['flip_x'] = (self._extra_kwds['flip_x']
1218
+ or other._extra_kwds['flip_x'])
1219
+ if 'flip_y' in self._extra_kwds and 'flip_y' in other._extra_kwds:
1220
+ g._extra_kwds['flip_y'] = (self._extra_kwds['flip_y']
1221
+ or other._extra_kwds['flip_y'])
1222
+ if self.aspect_ratio() == 'automatic':
1223
+ g.set_aspect_ratio(other.aspect_ratio())
1224
+ elif other.aspect_ratio() == 'automatic':
1225
+ g.set_aspect_ratio(self.aspect_ratio())
1226
+ else:
1227
+ g.set_aspect_ratio(max(self.aspect_ratio(), other.aspect_ratio()))
1228
+ return g
1229
+
1230
+ def add_primitive(self, primitive):
1231
+ """
1232
+ Add a primitive to this graphics object.
1233
+
1234
+ EXAMPLES:
1235
+
1236
+ We give a very explicit example::
1237
+
1238
+ sage: G = Graphics()
1239
+ sage: from math import e
1240
+ sage: from sage.plot.line import Line
1241
+ sage: from sage.plot.arrow import Arrow
1242
+ sage: L = Line([3,4,2,7,-2], [1,2,e,4,5.],
1243
+ ....: {'alpha': 1, 'thickness': 2, 'rgbcolor': (0,1,1),
1244
+ ....: 'legend_label': ''})
1245
+ sage: A = Arrow(2, -5, .1, .2,
1246
+ ....: {'width': 3, 'head': 0, 'rgbcolor': (1,0,0),
1247
+ ....: 'linestyle': 'dashed', 'zorder': 8, 'legend_label': ''})
1248
+ sage: G.add_primitive(L)
1249
+ sage: G.add_primitive(A)
1250
+ sage: G
1251
+ Graphics object consisting of 2 graphics primitives
1252
+ """
1253
+ self._objects.append(primitive)
1254
+
1255
+ def plot(self):
1256
+ """
1257
+ Draw a 2D plot of this graphics object, which just returns this
1258
+ object since this is already a 2D graphics object.
1259
+
1260
+ EXAMPLES::
1261
+
1262
+ sage: S = circle((0,0), 2)
1263
+ sage: S.plot() is S
1264
+ True
1265
+
1266
+ It does not accept any argument (:issue:`19539`)::
1267
+
1268
+ sage: S.plot(1)
1269
+ Traceback (most recent call last):
1270
+ ...
1271
+ TypeError: ...plot() takes 1 positional argument but 2 were given
1272
+
1273
+ sage: S.plot(hey='hou')
1274
+ Traceback (most recent call last):
1275
+ ...
1276
+ TypeError: ...plot() got an unexpected keyword argument 'hey'
1277
+ """
1278
+ return self
1279
+
1280
+ def plot3d(self, z=0, **kwds):
1281
+ """
1282
+ Return an embedding of this 2D plot into the xy-plane of 3D space,
1283
+ as a 3D plot object. An optional parameter ``z`` can be given to
1284
+ specify the z-coordinate.
1285
+
1286
+ EXAMPLES::
1287
+
1288
+ sage: sum(plot(z*sin(x), 0, 10).plot3d(z) # long time # needs sage.symbolic
1289
+ ....: for z in range(6))
1290
+ Graphics3d Object
1291
+ """
1292
+ from sage.plot.plot3d.base import Graphics3dGroup
1293
+ g = Graphics3dGroup([g.plot3d(**kwds) for g in self._objects])
1294
+ if z:
1295
+ g = g.translate(0, 0, z)
1296
+ return g
1297
+
1298
+ @classmethod
1299
+ def _extract_kwds_for_show(cls, kwds, ignore=[]):
1300
+ """
1301
+ Extract keywords relevant to show() from the provided dictionary.
1302
+
1303
+ EXAMPLES::
1304
+
1305
+ sage: kwds = {'f': lambda x: x, 'xmin': 0, 'figsize': [1,1], 'plot_points': (40, 40)}
1306
+ sage: G_kwds = Graphics._extract_kwds_for_show(kwds, ignore='xmin')
1307
+ sage: kwds # Note how this action modifies the passed dictionary
1308
+ {'f': <function <lambda> at 0x...>,
1309
+ 'plot_points': (40, 40),
1310
+ 'xmin': 0}
1311
+ sage: G_kwds
1312
+ {'figsize': [1, 1]}
1313
+
1314
+ This method is intended to be used with _set_extra_kwds(). Here is an
1315
+ idiom to ensure the correct keywords will get passed on to show()::
1316
+
1317
+ sage: options = {} # Usually this will come from an argument
1318
+ sage: g = Graphics()
1319
+ sage: g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
1320
+ """
1321
+ result = {}
1322
+ for option in cls.SHOW_OPTIONS:
1323
+ if option not in ignore:
1324
+ try:
1325
+ result[option] = kwds.pop(option)
1326
+ except KeyError:
1327
+ pass
1328
+ return result
1329
+
1330
+ def _set_extra_kwds(self, kwds):
1331
+ """
1332
+ Set a dictionary of keywords that will get passed on to show().
1333
+
1334
+ TESTS::
1335
+
1336
+ sage: g = Graphics()
1337
+ sage: g._extra_kwds
1338
+ {}
1339
+ sage: g._set_extra_kwds({'figsize': [10,10]})
1340
+ sage: g._extra_kwds
1341
+ {'figsize': [10, 10]}
1342
+ sage: g.show() # Now the (blank) plot will be extra large
1343
+ """
1344
+ self._extra_kwds = kwds
1345
+
1346
+ def _set_scale(self, subplot, scale=None, base=None):
1347
+ """
1348
+ Set the scale of the axes in the current subplot. This function is
1349
+ only for internal use.
1350
+
1351
+ INPUT:
1352
+
1353
+ - ``subplot`` -- matplotlib Axes instance
1354
+ - ``scale`` -- the scale of the figure. Values it can take are
1355
+ ``'linear'``, ``'loglog'``, ``'semilogx'``, ``'semilogy'``. See
1356
+ :meth:`show` for other options it can take.
1357
+ - ``base`` -- the base of the logarithm if a logarithmic scale is
1358
+ set. See :meth:`show` for the options it can take
1359
+
1360
+ OUTPUT:
1361
+
1362
+ The scale in the form of a tuple: (xscale, yscale, basex, basey)
1363
+
1364
+ EXAMPLES::
1365
+
1366
+ sage: # needs sage.symbolic
1367
+ sage: p = plot(x, 1, 10)
1368
+ sage: fig = p.matplotlib()
1369
+ sage: ax = fig.get_axes()[0]
1370
+ sage: p._set_scale(ax, scale='linear', base=2)
1371
+ ('linear', 'linear', 10, 10)
1372
+ sage: p._set_scale(ax, scale='semilogy', base=2)
1373
+ ('linear', 'log', 10, 2)
1374
+ sage: p._set_scale(ax, scale=('loglog', 2, 3))
1375
+ ('log', 'log', 2, 3)
1376
+ sage: p._set_scale(ax, scale=['semilogx', 2])
1377
+ ('log', 'linear', 2, 10)
1378
+
1379
+ TESTS::
1380
+
1381
+ sage: # needs sage.symbolic
1382
+ sage: p._set_scale(ax, 'log')
1383
+ Traceback (most recent call last):
1384
+ ...
1385
+ ValueError: The scale must be one of 'linear', 'loglog', 'semilogx' or 'semilogy' -- got 'log'
1386
+ sage: p._set_scale(ax, ('loglog', 1))
1387
+ Traceback (most recent call last):
1388
+ ...
1389
+ ValueError: The base of the logarithm must be greater than 1
1390
+ """
1391
+ if scale is None:
1392
+ return ('linear', 'linear', 10, 10)
1393
+ if isinstance(scale, (list, tuple)):
1394
+ if len(scale) != 2 and len(scale) != 3:
1395
+ raise ValueError("If the input is a tuple, it must be of "
1396
+ "the form (scale, base) or (scale, basex, basey)")
1397
+ if len(scale) == 2:
1398
+ base = scale[1]
1399
+ else:
1400
+ base = scale[1:]
1401
+ scale = scale[0]
1402
+
1403
+ if scale not in ('linear', 'loglog', 'semilogx', 'semilogy'):
1404
+ raise ValueError("The scale must be one of 'linear', 'loglog',"
1405
+ f" 'semilogx' or 'semilogy' -- got '{scale}'")
1406
+
1407
+ if isinstance(base, (list, tuple)):
1408
+ basex, basey = base
1409
+ elif base is None:
1410
+ basex = basey = 10
1411
+ else:
1412
+ basex = basey = base
1413
+
1414
+ if basex <= 1 or basey <= 1:
1415
+ raise ValueError("The base of the logarithm must be greater "
1416
+ "than 1")
1417
+
1418
+ xscale = yscale = 'linear'
1419
+ if scale == 'linear':
1420
+ basex = basey = 10
1421
+ elif scale == 'loglog':
1422
+ subplot.set_xscale('log', base=basex)
1423
+ subplot.set_yscale('log', base=basey)
1424
+ xscale = yscale = 'log'
1425
+ elif scale == 'semilogx':
1426
+ subplot.set_xscale('log', base=basex)
1427
+ basey = 10
1428
+ xscale = 'log'
1429
+ elif scale == 'semilogy':
1430
+ subplot.set_yscale('log', base=basey)
1431
+ basex = 10
1432
+ yscale = 'log'
1433
+
1434
+ return (xscale, yscale, basex, basey)
1435
+
1436
+ # This dictionary has the default values for the keywords to show(). When
1437
+ # show is invoked with keyword arguments, those arguments are merged with
1438
+ # this dictionary to create a set of keywords with the defaults filled in.
1439
+ # Then, those keywords are passed on to save().
1440
+
1441
+ # NOTE: If you intend to use a new parameter in show(), you should update
1442
+ # this dictionary to contain the default value for that parameter.
1443
+
1444
+ SHOW_OPTIONS = { # axes options
1445
+ 'axes': None, 'axes_labels': None, 'axes_labels_size': None,
1446
+ 'axes_pad': None, 'base': None, 'scale': None,
1447
+ 'xmin': None, 'xmax': None, 'ymin': None, 'ymax': None,
1448
+ 'flip_x': False, 'flip_y': False,
1449
+ # Figure options
1450
+ 'aspect_ratio': None, 'dpi': DEFAULT_DPI, 'fig_tight': True,
1451
+ 'figsize': None, 'fontsize': None, 'frame': False,
1452
+ 'title': None, 'title_pos': None, 'transparent': False,
1453
+ # Grid options
1454
+ 'gridlines': None, 'gridlinesstyle': None,
1455
+ 'hgridlinesstyle': None, 'vgridlinesstyle': None,
1456
+ # Legend options
1457
+ 'legend_options': {}, 'show_legend': None,
1458
+ # Ticks options
1459
+ 'ticks': None, 'tick_formatter': None, 'ticks_integer': False,
1460
+ # Text options
1461
+ 'typeset': 'default'}
1462
+
1463
+ # Default options for the legends:
1464
+
1465
+ LEGEND_OPTIONS = {'back_color': 'white', 'borderpad': 0.6,
1466
+ 'borderaxespad': None,
1467
+ 'columnspacing': None,
1468
+ 'fancybox': False, 'font_family': 'sans-serif',
1469
+ 'font_size': 'medium', 'font_style': 'normal',
1470
+ 'font_variant': 'normal', 'font_weight': 'medium',
1471
+ 'handlelength': 0.05, 'handletextpad': 0.5,
1472
+ 'labelspacing': 0.02, 'loc': 'best',
1473
+ 'markerscale': 0.6, 'ncol': 1, 'numpoints': 2,
1474
+ 'shadow': True, 'title': None}
1475
+
1476
+ @suboptions('legend', **LEGEND_OPTIONS)
1477
+ def show(self, **kwds):
1478
+ r"""
1479
+ Show this graphics image immediately.
1480
+
1481
+ This method attempts to display the graphics immediately,
1482
+ without waiting for the currently running code (if any) to
1483
+ return to the command line. Be careful, calling it from within
1484
+ a loop will potentially launch a large number of external
1485
+ viewer programs.
1486
+
1487
+ OPTIONAL INPUT:
1488
+
1489
+ - ``dpi`` -- (default: 100) dots per inch
1490
+
1491
+ - ``figsize`` -- (default: [6.4, 4.8]) [width, height] inches. The
1492
+ maximum value of each of the width and the height can be 327
1493
+ inches, at the default ``dpi`` of 100 dpi, which is just shy of
1494
+ the maximum allowed value of 32768 dots (pixels).
1495
+
1496
+ - ``fig_tight`` -- boolean (default: ``True``); whether to clip the drawing
1497
+ tightly around drawn objects. If ``True``, then the resulting
1498
+ image will usually not have dimensions corresponding to
1499
+ ``figsize``. If ``False``, the resulting image will have
1500
+ dimensions corresponding to ``figsize``.
1501
+
1502
+ - ``aspect_ratio`` -- the perceived height divided by the
1503
+ perceived width. For example, if the aspect ratio is set to ``1``, circles
1504
+ will look round and a unit square will appear to have sides
1505
+ of equal length, and if the aspect ratio is set ``2``, vertical units will be
1506
+ twice as long as horizontal units, so a unit square will be twice as
1507
+ high as it is wide. If set to ``'automatic'``, the aspect ratio
1508
+ is determined by ``figsize`` and the picture fills the figure.
1509
+
1510
+ - ``axes`` -- (default: ``True``)
1511
+
1512
+ - ``axes_labels`` -- (default: ``None``) list (or tuple) of two
1513
+ strings; the first is used as the label for the horizontal
1514
+ axis, and the second for the vertical axis.
1515
+
1516
+ - ``axes_labels_size`` -- (default: current setting -- 1.6) scale factor
1517
+ relating the size of the axes labels with respect to the size of the
1518
+ tick marks.
1519
+
1520
+ - ``fontsize`` -- (default: current setting -- 10) positive
1521
+ integer; used for axes labels; if you make this very large,
1522
+ you may have to increase figsize to see all labels.
1523
+
1524
+ - ``frame`` -- boolean (default: ``False``); draw a frame around the image
1525
+
1526
+ - ``gridlines`` -- (default: ``None``) can be any of the following:
1527
+
1528
+ - None, False: do not add grid lines.
1529
+
1530
+ - True, "automatic", "major": add grid lines at major ticks of the axes.
1531
+
1532
+ - "minor": add grid at major and minor ticks.
1533
+
1534
+ - [xlist,ylist]: a tuple or list containing
1535
+ two elements, where xlist (or ylist) can be
1536
+ any of the following.
1537
+
1538
+
1539
+ - None, False: don't add horizontal (or vertical) lines.
1540
+
1541
+ - True, "automatic", "major": add horizontal (or vertical) grid lines at
1542
+ the major ticks of the axes.
1543
+
1544
+ - "minor": add horizontal (or vertical) grid lines at major and minor ticks of
1545
+ axes.
1546
+
1547
+ - an iterable yielding numbers n or pairs (n,opts), where n
1548
+ is the coordinate of the line and opt is a dictionary of
1549
+ MATPLOTLIB options for rendering the line.
1550
+
1551
+
1552
+ - ``gridlinesstyle, hgridlinesstyle, vgridlinesstyle`` -
1553
+ (default: ``None``); a dictionary of MATPLOTLIB options for the
1554
+ rendering of the grid lines, the horizontal grid lines or the
1555
+ vertical grid lines, respectively.
1556
+
1557
+ - ``transparent`` -- boolean (default: ``False``); if True, make the
1558
+ background transparent
1559
+
1560
+ - ``axes_pad`` -- (default: 0.02 on ``'linear'`` scale, 1 on
1561
+ ``'log'`` scale)
1562
+
1563
+ - In the ``'linear'`` scale, it determines the percentage of the
1564
+ axis range that is added to each end of each axis. This helps
1565
+ avoid problems like clipping lines because of line-width, etc.
1566
+ To get axes that are exactly the specified limits, set
1567
+ ``axes_pad`` to zero.
1568
+
1569
+ - On the ``'log'`` scale, it determines the exponent of the
1570
+ fraction of the minimum (resp. maximum) that is subtracted from
1571
+ the minimum (resp. added to the maximum) value of the axis. For
1572
+ instance if the minimum is `m` and the base of the axis is `b`
1573
+ then the new minimum after padding the axis will be
1574
+ `m - m/b^{\mathrm{axes\_pad}}`.
1575
+
1576
+ - ``ticks_integer`` -- boolean (default: ``False``); guarantee that the ticks
1577
+ are integers (the ``ticks`` option, if specified, will
1578
+ override this)
1579
+
1580
+ - ``ticks`` -- a matplotlib locator for the major ticks, or
1581
+ a number. There are several options. For more information about
1582
+ locators, type ``from matplotlib import ticker`` and then
1583
+ ``ticker?``.
1584
+
1585
+ - If this is a locator object, then it is the locator for
1586
+ the horizontal axis. A value of None means use the default
1587
+ locator.
1588
+
1589
+ - If it is a list of two locators, then the first is for the
1590
+ horizontal axis and one for the vertical axis. A value of
1591
+ None means use the default locator (so a value of
1592
+ [None, my_locator] uses my_locator for the vertical axis and
1593
+ the default for the horizontal axis).
1594
+
1595
+ - If in either case above one of the entries is a number `m`
1596
+ (something which can be coerced to a float), it will be
1597
+ replaced by a MultipleLocator which places major ticks at
1598
+ integer multiples of `m`. See examples.
1599
+
1600
+ - If in either case above one of the entries is a list of
1601
+ numbers, it will be replaced by a FixedLocator which places
1602
+ ticks at the locations specified. This includes the case of
1603
+ of the empty list, which will give no ticks. See examples.
1604
+
1605
+ - ``tick_formatter`` -- a matplotlib formatter for the major
1606
+ ticks. There are several options. For more information about
1607
+ formatters, type ``from matplotlib import ticker`` and then
1608
+ ``ticker?``.
1609
+
1610
+ If the value of this keyword is a single item, then this will
1611
+ give the formatting for the horizontal axis *only* (except for
1612
+ the ``'latex'`` option). If it is a list or tuple, the first
1613
+ is for the horizontal axis, the second for the vertical axis.
1614
+ The options are below:
1615
+
1616
+ - If one of the entries is a formatter object, then it used.
1617
+ A value of None means to use the default locator (so using
1618
+ ``tick_formatter=[None, my_formatter]`` uses my_formatter
1619
+ for the vertical axis and the default for the horizontal axis).
1620
+
1621
+ - If one of the entries is a symbolic constant such as `\pi`,
1622
+ `e`, or `sqrt(2)`, ticks will be formatted nicely at rational
1623
+ multiples of this constant.
1624
+
1625
+ .. warning::
1626
+
1627
+ This should only be used with the ``ticks`` option using nice
1628
+ rational multiples of that constant!
1629
+
1630
+ - If one of the entries is the string ``'latex'``, then the
1631
+ formatting will be nice typesetting of the ticks. This is
1632
+ intended to be used when the tick locator for at least one of
1633
+ the axes is a list including some symbolic elements. This uses
1634
+ matplotlib's internal LaTeX rendering engine. If you want to
1635
+ use an external LaTeX compiler, then set the keyword option
1636
+ ``typeset``. See examples.
1637
+
1638
+ - ``title`` -- (default: ``None``) the title for the plot
1639
+
1640
+ - ``title_pos`` -- (default: ``None``) the position of the title for the
1641
+ plot. It must be a tuple or a list of two real numbers
1642
+ ``(x_pos, y_pos)`` which indicate the relative position of the
1643
+ title within the plot. The plot itself can be considered to
1644
+ occupy, in relative terms, the region within a unit square
1645
+ `[0, 1] \times [0, 1]`. The title text is centered around the
1646
+ horizontal factor ``x_pos`` of the plot. The baseline of the
1647
+ title text is present at the vertical factor ``y_pos`` of the
1648
+ plot. Hence, ``title_pos=(0.5, 0.5)`` will center the title in
1649
+ the plot, whereas ``title_pos=(0.5, 1.1)`` will center the
1650
+ title along the horizontal direction, but will place the title
1651
+ a fraction `0.1` times above the plot.
1652
+
1653
+ - If the first entry is a list of strings (or numbers), then the
1654
+ formatting for the horizontal axis will be typeset with the strings
1655
+ present in the list. Each entry of the list of strings must be
1656
+ provided with a corresponding number in the first entry of
1657
+ ``ticks`` to indicate its position on the axis. To typeset the
1658
+ strings with ``'latex'`` enclose them within ``'$'`` symbols. To
1659
+ have similar custom formatting of the labels along the vertical
1660
+ axis, the second entry must be a list of strings and the second
1661
+ entry of ``ticks`` must also be a list of numbers which give the
1662
+ positions of the labels. See the examples below.
1663
+
1664
+ - ``show_legend`` -- (default: ``None``) if ``True``, show the legend
1665
+
1666
+ - ``legend_*`` -- all the options valid for :meth:`set_legend_options`
1667
+ prefixed with ``legend_``
1668
+
1669
+ - ``base`` -- (default: 10) the base of the logarithm if
1670
+ a logarithmic scale is set. This must be greater than 1. The base
1671
+ can be also given as a list or tuple ``(basex, basey)``.
1672
+ ``basex`` sets the base of the logarithm along the horizontal
1673
+ axis and ``basey`` sets the base along the vertical axis.
1674
+
1675
+ - ``scale`` -- (default: ``'linear'``) string. The scale of the axes.
1676
+ Possible values are
1677
+
1678
+ - ``'linear'`` -- linear scaling of both the axes
1679
+ - ``'loglog'`` -- sets both the horizontal and vertical axes to
1680
+ logarithmic scale
1681
+ - ``'semilogx'`` -- sets only the horizontal axis to logarithmic
1682
+ scale
1683
+ - ``'semilogy'`` -- sets only the vertical axis to logarithmic
1684
+ scale
1685
+
1686
+ The scale can be also be given as single argument that is a list
1687
+ or tuple ``(scale, base)`` or ``(scale, basex, basey)``.
1688
+
1689
+ .. NOTE::
1690
+
1691
+ - If the ``scale`` is ``'linear'``, then irrespective of what
1692
+ ``base`` is set to, it will default to 10 and will remain
1693
+ unused.
1694
+
1695
+ - ``xmin`` -- starting x value in the rendered figure
1696
+
1697
+ - ``xmax`` -- ending x value in the rendered figure
1698
+
1699
+ - ``ymin`` -- starting y value in the rendered figure
1700
+
1701
+ - ``ymax`` -- ending y value in the rendered figure
1702
+
1703
+ - ``flip_x`` -- boolean (default: ``False``); if ``True``, flip the horizontal
1704
+ axis
1705
+
1706
+ - ``flip_y`` -- boolean (default: ``False``); if ``True``, flip the vertical
1707
+ axis
1708
+
1709
+ - ``typeset`` -- (default: ``'default'``) string. The type of
1710
+ font rendering that should be used for the text. The possible
1711
+ values are
1712
+
1713
+ - ``'default'`` -- uses matplotlib's internal text rendering
1714
+ engine called Mathtext ( see
1715
+ https://matplotlib.org/users/mathtext.html ). If you have
1716
+ modified the default matplotlib settings, for instance via
1717
+ a matplotlibrc file, then this option will not change any of
1718
+ those settings.
1719
+ - ``'latex'`` -- laTeX is used for rendering the fonts. This
1720
+ requires LaTeX, dvipng and Ghostscript to be installed
1721
+ - ``'type1'`` -- type 1 fonts are used by matplotlib in the text
1722
+ in the figure. This requires LaTeX, dvipng and Ghostscript to
1723
+ be installed.
1724
+
1725
+ OUTPUT:
1726
+
1727
+ This method does not return anything. Use :meth:`save` if you
1728
+ want to save the figure as an image.
1729
+
1730
+ EXAMPLES::
1731
+
1732
+ sage: c = circle((1,1), 1, color='red')
1733
+ sage: c.show(xmin=-1, xmax=3, ymin=-1, ymax=3)
1734
+
1735
+ You can make the picture larger by changing ``figsize`` with width,
1736
+ height each having a maximum value of 327 inches at default dpi::
1737
+
1738
+ sage: p = ellipse((0,0),4,1)
1739
+ sage: p.show(figsize=[327,10], dpi=100)
1740
+ sage: p.show(figsize=[328,10], dpi=80)
1741
+
1742
+ You can turn off the drawing of the axes::
1743
+
1744
+ sage: show(plot(sin,-4,4), axes=False) # needs sage.symbolic
1745
+
1746
+ You can also label the axes. Putting something in dollar
1747
+ signs formats it as a mathematical expression::
1748
+
1749
+ sage: show(plot(sin,-4,4), axes_labels=('$x$','$y$')) # needs sage.symbolic
1750
+
1751
+ You can add a title to a plot::
1752
+
1753
+ sage: show(plot(sin,-4,4), title=r'A plot of $\sin(x)$') # needs sage.symbolic
1754
+
1755
+ You can also provide the position for the title to the plot. In the
1756
+ plot below the title is placed on the bottom left of the figure.::
1757
+
1758
+ sage: plot(sin, -4, 4, title='Plot sin(x)', title_pos=(0.05,-0.05)) # needs sage.symbolic
1759
+ Graphics object consisting of 1 graphics primitive
1760
+
1761
+ If you want all the text to be rendered by using an external LaTeX
1762
+ installation then set the ``typeset`` to ``'latex'``. This
1763
+ requires that LaTeX, dvipng and Ghostscript be installed::
1764
+
1765
+ sage: plot(x, typeset='latex') # optional - latex, needs sage.symbolic
1766
+ Graphics object consisting of 1 graphics primitive
1767
+
1768
+ If you want all the text in your plot to use Type 1 fonts, then
1769
+ set the ``typeset`` option to ``'type1'``. This requires that
1770
+ LaTeX, dvipng and Ghostscript be installed::
1771
+
1772
+ sage: plot(x, typeset='type1') # optional - latex, needs sage.symbolic
1773
+ Graphics object consisting of 1 graphics primitive
1774
+
1775
+ You can turn on the drawing of a frame around the plots::
1776
+
1777
+ sage: show(plot(sin,-4,4), frame=True) # needs sage.symbolic
1778
+
1779
+ You can make the background transparent::
1780
+
1781
+ sage: plot(sin(x), (x, -4, 4), transparent=True) # needs sage.symbolic
1782
+ Graphics object consisting of 1 graphics primitive
1783
+
1784
+ Prior to :issue:`19485`, legends by default had a shadowless gray
1785
+ background. This behavior can be recovered by passing in certain
1786
+ ``legend_options``::
1787
+
1788
+ sage: p = plot(sin(x), legend_label=r'$\sin(x)$') # needs sage.symbolic
1789
+ sage: p.show(legend_options={'back_color': (0.9,0.9,0.9), # needs sage.symbolic
1790
+ ....: 'shadow': False})
1791
+
1792
+ We can change the scale of the axes in the graphics before
1793
+ displaying::
1794
+
1795
+ sage: G = plot(exp, 1, 10) # needs sage.symbolic
1796
+ sage: G.show(scale='semilogy') # needs sage.symbolic
1797
+
1798
+ We can change the base of the logarithm too. The following changes
1799
+ the vertical axis to be on log scale, and with base 2. Note that
1800
+ the ``base`` argument will ignore any changes to the axis which is
1801
+ in linear scale.::
1802
+
1803
+ sage: G.show(scale='semilogy', base=2) # y axis as powers of 2 # long time, needs sage.symbolic
1804
+
1805
+ ::
1806
+
1807
+ sage: G.show(scale='semilogy', base=(3,2)) # base ignored for x-axis # needs sage.symbolic
1808
+
1809
+ The scale can be also given as a 2-tuple or a 3-tuple.::
1810
+
1811
+ sage: G.show(scale=('loglog', 2.1)) # both x and y axes in base 2.1 # long time, needs sage.symbolic
1812
+
1813
+ ::
1814
+
1815
+ sage: G.show(scale=('loglog', 2, 3)) # x in base 2, y in base 3 # long time, needs sage.symbolic
1816
+
1817
+ The base need not be an integer, though it does have to be made
1818
+ a float.::
1819
+
1820
+ sage: G.show(scale='semilogx', base=float(e)) # base is e # needs sage.symbolic
1821
+
1822
+ Logarithmic scale can be used for various kinds of plots. Here are
1823
+ some examples.::
1824
+
1825
+ sage: G = list_plot([10**i for i in range(10)]) # long time, needs sage.symbolic
1826
+ sage: G.show(scale='semilogy') # long time, needs sage.symbolic
1827
+
1828
+ ::
1829
+
1830
+ sage: G = parametric_plot((x, x**2), (x, 1, 10)) # needs sage.symbolic
1831
+ sage: G.show(scale='loglog') # needs sage.symbolic
1832
+
1833
+ ::
1834
+
1835
+ sage: disk((5,5), 4, (0, 3*pi/2)).show(scale='loglog',base=2) # needs sage.symbolic
1836
+
1837
+ ::
1838
+
1839
+ sage: x, y = var('x, y') # needs sage.symbolic
1840
+ sage: G = plot_vector_field((2^x,y^2), (x,1,10), (y,1,100)) # needs sage.symbolic
1841
+ sage: G.show(scale='semilogx',base=2) # needs sage.symbolic
1842
+
1843
+ Flip the horizontal or vertical axis.
1844
+
1845
+ ::
1846
+
1847
+ sage: G = plot(x^3, -2, 3) # needs sage.symbolic
1848
+ sage: G.show(flip_x=True) # needs sage.symbolic
1849
+ sage: G.show(flip_y=True) # needs sage.symbolic
1850
+
1851
+ Add grid lines at the major ticks of the axes.
1852
+
1853
+ ::
1854
+
1855
+ sage: c = circle((0,0), 1)
1856
+ sage: c.show(gridlines=True)
1857
+ sage: c.show(gridlines='automatic')
1858
+ sage: c.show(gridlines='major')
1859
+
1860
+ Add grid lines at the major and minor ticks of the axes.
1861
+
1862
+ ::
1863
+
1864
+ sage: # needs sage.symbolic
1865
+ sage: u,v = var('u v')
1866
+ sage: f = exp(-(u^2+v^2))
1867
+ sage: p = plot_vector_field(f.gradient(), (u,-2,2), (v,-2,2))
1868
+ sage: p.show(gridlines='minor')
1869
+
1870
+ Add only horizontal or vertical grid lines.
1871
+
1872
+ ::
1873
+
1874
+ sage: p = plot(sin, -10, 20) # needs sage.symbolic
1875
+ sage: p.show(gridlines=[None, "automatic"]) # needs sage.symbolic
1876
+ sage: p.show(gridlines=["minor", False]) # needs sage.symbolic
1877
+
1878
+ Add grid lines at specific positions (using lists/tuples).
1879
+
1880
+ ::
1881
+
1882
+ sage: x, y = var('x, y') # needs sage.symbolic
1883
+ sage: p = implicit_plot((y^2-x^2)*(x-1)*(2*x-3) - 4*(x^2+y^2-2*x)^2, # needs sage.symbolic
1884
+ ....: (x,-2,2), (y,-2,2), plot_points=1000)
1885
+ sage: p.show(gridlines=[[1,0],[-1,0,1]]) # needs sage.symbolic
1886
+
1887
+ Add grid lines at specific positions (using iterators).
1888
+
1889
+ ::
1890
+
1891
+ sage: def maple_leaf(t):
1892
+ ....: return (100/(100+(t-pi/2)^8))*(2-sin(7*t)-cos(30*t)/2)
1893
+ sage: p = polar_plot(maple_leaf, -pi/4, 3*pi/2, # long time, needs sage.symbolic
1894
+ ....: color='red',plot_points=1000)
1895
+ sage: p.show(gridlines=([-3,-2.75,..,3], range(-1,5,2))) # long time, needs sage.symbolic
1896
+
1897
+ Add grid lines at specific positions (using functions).
1898
+
1899
+ ::
1900
+
1901
+ sage: # needs sage.symbolic
1902
+ sage: y = x^5 + 4*x^4 - 10*x^3 - 40*x^2 + 9*x + 36
1903
+ sage: p = plot(y, -4.1, 1.1)
1904
+ sage: xlines = lambda a, b: [z for z, m in y.roots()]
1905
+ sage: p.show(gridlines=[xlines, [0]], frame=True, axes=False)
1906
+
1907
+ Change the style of all the grid lines.
1908
+
1909
+ ::
1910
+
1911
+ sage: b = bar_chart([-3,5,-6,11], color='red')
1912
+ sage: b.show(gridlines=([-1,-0.5,..,4], True),
1913
+ ....: gridlinesstyle=dict(color='blue', linestyle=':'))
1914
+
1915
+ Change the style of the horizontal or vertical grid lines
1916
+ separately.
1917
+
1918
+ ::
1919
+
1920
+ sage: p = polar_plot(2 + 2*cos(x), 0, 2*pi, color=hue(0.3)) # needs sage.symbolic
1921
+ sage: p.show(gridlines=True, # needs sage.symbolic
1922
+ ....: hgridlinesstyle=dict(color='orange', linewidth=1.0),
1923
+ ....: vgridlinesstyle=dict(color='blue', linestyle=':'))
1924
+
1925
+ Change the style of each grid line individually.
1926
+
1927
+ ::
1928
+
1929
+ sage: x, y = var('x, y') # needs sage.symbolic
1930
+ sage: p = implicit_plot((y^2-x^2)*(x-1)*(2*x-3) - 4*(x^2+y^2-2*x)^2, # needs sage.symbolic
1931
+ ....: (x,-2,2), (y,-2,2), plot_points=1000)
1932
+ sage: p.show(gridlines=( # needs sage.symbolic
1933
+ ....: [
1934
+ ....: (1,{"color":"red","linestyle":":"}),
1935
+ ....: (0,{"color":"blue","linestyle":"--"})
1936
+ ....: ],
1937
+ ....: [
1938
+ ....: (-1,{"color":"red","linestyle":":"}),
1939
+ ....: (0,{"color":"blue","linestyle":"--"}),
1940
+ ....: (1,{"color":"red","linestyle":":"}),
1941
+ ....: ]
1942
+ ....: ),
1943
+ ....: gridlinesstyle=dict(marker='x',color='black'))
1944
+
1945
+ Grid lines can be added to contour plots.
1946
+
1947
+ ::
1948
+
1949
+ sage: f = sin(x^2 + y^2)*cos(x)*sin(y) # needs sage.symbolic
1950
+ sage: c = contour_plot(f, (x, -4, 4), (y, -4, 4), plot_points=100) # needs sage.symbolic
1951
+ sage: c.show(gridlines=True, # needs sage.symbolic
1952
+ ....: gridlinesstyle={'linestyle': ':', 'linewidth': 1, 'color': 'red'})
1953
+
1954
+ Grid lines can be added to matrix plots.
1955
+
1956
+ ::
1957
+
1958
+ sage: M = MatrixSpace(QQ,10).random_element()
1959
+ sage: matrix_plot(M).show(gridlines=True)
1960
+
1961
+ By default, Sage increases the horizontal and vertical axes
1962
+ limits by a certain percentage in all directions. This is
1963
+ controlled by the ``axes_pad`` parameter. Increasing the range
1964
+ of the axes helps avoid problems with lines and dots being
1965
+ clipped because the linewidth extends beyond the axes. To get
1966
+ axes limits that are exactly what is specified, set
1967
+ ``axes_pad`` to zero. Compare the following two examples
1968
+
1969
+ ::
1970
+
1971
+ sage: (plot(sin(x), (x, -pi, pi), thickness=2) # needs sage.symbolic
1972
+ ....: + point((pi, -1), pointsize=15))
1973
+ Graphics object consisting of 2 graphics primitives
1974
+ sage: (plot(sin(x), (x, -pi, pi), thickness=2, axes_pad=0) # needs sage.symbolic
1975
+ ....: + point((pi, -1), pointsize=15))
1976
+ Graphics object consisting of 2 graphics primitives
1977
+
1978
+ The behavior of the ``axes_pad`` parameter is different if the axis
1979
+ is in the ``'log'`` scale. If `b` is the base of the axis, the
1980
+ minimum value of the axis, is decreased by the factor
1981
+ `1/b^{\mathrm{axes\_pad}}` of the minimum and the maximum value of the axis
1982
+ is increased by the same factor of the maximum value. Compare the
1983
+ axes in the following two plots to see the difference.
1984
+
1985
+ ::
1986
+
1987
+ sage: plot_loglog(x, (1.1*10**-2, 9990)) # needs sage.symbolic
1988
+ Graphics object consisting of 1 graphics primitive
1989
+
1990
+ sage: plot_loglog(x, (1.1*10**-2, 9990), axes_pad=0) # needs sage.symbolic
1991
+ Graphics object consisting of 1 graphics primitive
1992
+
1993
+ Via matplotlib, Sage allows setting of custom ticks. See above
1994
+ for more details.
1995
+
1996
+ Here the labels are not so useful::
1997
+
1998
+ sage: plot(sin(pi*x), (x, -8, 8)) # needs sage.symbolic
1999
+ Graphics object consisting of 1 graphics primitive
2000
+
2001
+ Now put ticks at multiples of 2::
2002
+
2003
+ sage: plot(sin(pi*x), (x, -8, 8), ticks=2) # needs sage.symbolic
2004
+ Graphics object consisting of 1 graphics primitive
2005
+
2006
+ Or just choose where you want the ticks::
2007
+
2008
+ sage: plot(sin(pi*x), (x, -8, 8), ticks=[[-7,-3,0,3,7], [-1/2,0,1/2]]) # needs sage.symbolic
2009
+ Graphics object consisting of 1 graphics primitive
2010
+
2011
+ Or no ticks at all::
2012
+
2013
+ sage: plot(sin(pi*x), (x, -8, 8), ticks=[[], []]) # needs sage.symbolic
2014
+ Graphics object consisting of 1 graphics primitive
2015
+
2016
+ This can be very helpful in showing certain features of plots. ::
2017
+
2018
+ sage: plot(1.5/(1+e^(-x)), (x, -10, 10)) # doesn't quite show value of inflection point # needs sage.symbolic
2019
+ Graphics object consisting of 1 graphics primitive
2020
+
2021
+ ::
2022
+
2023
+ sage: plot(1.5/(1+e^(-x)), (x, -10, 10), # It's right at f(x)=0.75! # needs sage.symbolic
2024
+ ....: ticks=[None, 1.5/4])
2025
+ Graphics object consisting of 1 graphics primitive
2026
+
2027
+ But be careful to leave enough room for at least two major ticks, so that
2028
+ the user can tell what the scale is::
2029
+
2030
+ sage: plot(x^2, (x,1,8), ticks=6).show() # needs sage.symbolic
2031
+ Traceback (most recent call last):
2032
+ ...
2033
+ ValueError: Expand the range of the independent variable to
2034
+ allow two multiples of your tick locator (option `ticks`).
2035
+
2036
+ We can also do custom formatting if you need it. See above for full
2037
+ details::
2038
+
2039
+ sage: plot(2*x + 1, (x,0,5), # not tested (broken with matplotlib 3.6), needs sage.symbolic
2040
+ ....: ticks=[[0,1,e,pi,sqrt(20)], 2],
2041
+ ....: tick_formatter='latex')
2042
+ Graphics object consisting of 1 graphics primitive
2043
+
2044
+ This is particularly useful when setting custom ticks in multiples
2045
+ of `\pi`.
2046
+
2047
+ ::
2048
+
2049
+ sage: plot(sin(x), (x,0,2*pi), ticks=pi/3, tick_formatter=pi) # needs sage.symbolic
2050
+ Graphics object consisting of 1 graphics primitive
2051
+
2052
+ But keep in mind that you will get exactly the formatting you asked
2053
+ for if you specify both formatters. The first syntax is recommended
2054
+ for best style in that case. ::
2055
+
2056
+ sage: plot(arcsin(x), (x,-1,1), ticks=[None, pi/6], # Nice-looking! # needs sage.symbolic
2057
+ ....: tick_formatter=["latex", pi])
2058
+ Graphics object consisting of 1 graphics primitive
2059
+
2060
+ ::
2061
+
2062
+ sage: plot(arcsin(x), (x,-1,1), ticks=[None, pi/6], # Not so nice-looking # needs sage.symbolic
2063
+ ....: tick_formatter=[None, pi])
2064
+ Graphics object consisting of 1 graphics primitive
2065
+
2066
+ Custom tick labels can be provided by providing the keyword
2067
+ ``tick_formatter`` with the list of labels, and simultaneously
2068
+ providing the keyword ``ticks`` with the positions of the labels. ::
2069
+
2070
+ sage: plot(x, (x,0,3), ticks=[[1,2.5], [0.5,1,2]], # needs sage.symbolic
2071
+ ....: tick_formatter=[["$x_1$","$x_2$"], ["$y_1$","$y_2$","$y_3$"]])
2072
+ Graphics object consisting of 1 graphics primitive
2073
+
2074
+ The following sets the custom tick labels only along the horizontal
2075
+ axis. ::
2076
+
2077
+ sage: plot(x**2, (x,0,2), ticks=[[1,2], None], # needs sage.symbolic
2078
+ ....: tick_formatter=[["$x_1$","$x_2$"], None])
2079
+ Graphics object consisting of 1 graphics primitive
2080
+
2081
+ If the number of tick labels do not match the number of positions of
2082
+ tick labels, then it results in an error.::
2083
+
2084
+ sage: plot(x**2, (x,0,2), ticks=[[2], None], # needs sage.symbolic
2085
+ ....: tick_formatter=[["$x_1$","$x_2$"], None]).show()
2086
+ Traceback (most recent call last):
2087
+ ...
2088
+ ValueError: If the first component of the list `tick_formatter` is a list
2089
+ then the first component of `ticks` must also be a list of equal length.
2090
+
2091
+ When using logarithmic scale along the axis, make sure to have
2092
+ enough room for two ticks so that the user can tell what the scale
2093
+ is. This can be effected by increasing the range of the independent
2094
+ variable, or by changing the ``base``, or by providing enough tick
2095
+ locations by using the ``ticks`` parameter.
2096
+
2097
+ By default, Sage will expand the variable range so that at least two
2098
+ ticks are included along the logarithmic axis. However, if you
2099
+ specify ``ticks`` manually, this safety measure can be defeated::
2100
+
2101
+ sage: list_plot_loglog([(1,2),(2,3)], plotjoined=True, ticks=[[1],[1]])
2102
+ doctest:...: UserWarning: The x-axis contains fewer than 2 ticks;
2103
+ the logarithmic scale of the plot may not be apparent to the reader.
2104
+ doctest:...: UserWarning: The y-axis contains fewer than 2 ticks;
2105
+ the logarithmic scale of the plot may not be apparent to the reader.
2106
+ Graphics object consisting of 1 graphics primitive
2107
+
2108
+ This one works, since the horizontal axis is automatically expanded
2109
+ to contain two ticks and the vertical axis is provided with two ticks::
2110
+
2111
+ sage: list_plot_loglog([(1,2),(2,3)], plotjoined=True, ticks=[None,[1,10]])
2112
+ Graphics object consisting of 1 graphics primitive
2113
+
2114
+ Another example in the log scale where both the axes are automatically
2115
+ expanded to show two major ticks::
2116
+
2117
+ sage: list_plot_loglog([(2,0.5), (3, 4)], plotjoined=True)
2118
+ Graphics object consisting of 1 graphics primitive
2119
+
2120
+ When using ``title_pos``, it must be ensured that a list or a tuple
2121
+ of length two is used. Otherwise, a warning is raised::
2122
+
2123
+ sage: plot(x, -4, 4, title='Plot x', title_pos=0.05) # needs sage.symbolic
2124
+ doctest:...: ...RichReprWarning: Exception in _rich_repr_ while displaying
2125
+ object: 'title_pos' must be a list or tuple of two real numbers.
2126
+ Graphics object consisting of 1 graphics primitive
2127
+
2128
+ TESTS:
2129
+
2130
+ The following tests result in a segmentation fault and should not
2131
+ be run or doctested::
2132
+
2133
+ sage: p = ellipse((0,0),4,1)
2134
+ sage: p.show(figsize=[232,232],dpi=100) # not tested
2135
+ ------------------------------------------------------------------------
2136
+ Unhandled SIGSEGV: A segmentation fault occurred.
2137
+ This probably occurred because a *compiled* module has a bug
2138
+ in it and is not properly wrapped with sig_on(), sig_off().
2139
+ Python will now terminate.
2140
+ ------------------------------------------------------------------------
2141
+ sage: p.show(figsize=[327,181],dpi=100) # not tested
2142
+ ------------------------------------------------------------------------
2143
+ Unhandled SIGSEGV: A segmentation fault occurred.
2144
+ This probably occurred because a *compiled* module has a bug
2145
+ in it and is not properly wrapped with sig_on(), sig_off().
2146
+ Python will now terminate.
2147
+ ------------------------------------------------------------------------
2148
+
2149
+ The following tests ensure we give a good error message for
2150
+ negative figsizes::
2151
+
2152
+ sage: # needs sage.symbolic
2153
+ sage: P = plot(x^2,(x,0,1))
2154
+ sage: P.show(figsize=[-1,1])
2155
+ Traceback (most recent call last):
2156
+ ...
2157
+ ValueError: figsize should be positive numbers, not -1.0 and 1.0
2158
+ sage: P.show(figsize=-1)
2159
+ Traceback (most recent call last):
2160
+ ...
2161
+ ValueError: figsize should be positive, not -1.0
2162
+ sage: P.show(figsize=x^2)
2163
+ Traceback (most recent call last):
2164
+ ...
2165
+ TypeError: figsize should be a positive number, not x^2
2166
+ sage: P.show(figsize=[2,3,4])
2167
+ Traceback (most recent call last):
2168
+ ...
2169
+ ValueError: figsize should be a positive number or
2170
+ a list of two positive numbers, not [2, 3, 4]
2171
+ sage: P.show(figsize=[sqrt(2),sqrt(3)])
2172
+ """
2173
+ from sage.repl.rich_output import get_display_manager
2174
+ dm = get_display_manager()
2175
+ dm.display_immediately(self, **kwds)
2176
+
2177
+ def xmin(self, xmin=None):
2178
+ """
2179
+ EXAMPLES::
2180
+
2181
+ sage: g = line([(-1,1), (3,2)])
2182
+ sage: g.xmin()
2183
+ -1.0
2184
+ sage: g.xmin(-3)
2185
+ sage: g.xmin()
2186
+ -3.0
2187
+ """
2188
+ if xmin is None:
2189
+ return self.get_axes_range()['xmin']
2190
+ else:
2191
+ self.set_axes_range(xmin=xmin)
2192
+
2193
+ def xmax(self, xmax=None):
2194
+ """
2195
+ EXAMPLES::
2196
+
2197
+ sage: g = line([(-1,1), (3,2)])
2198
+ sage: g.xmax()
2199
+ 3.0
2200
+ sage: g.xmax(10)
2201
+ sage: g.xmax()
2202
+ 10.0
2203
+ """
2204
+ if xmax is None:
2205
+ return self.get_axes_range()['xmax']
2206
+ else:
2207
+ self.set_axes_range(xmax=xmax)
2208
+
2209
+ def ymin(self, ymin=None):
2210
+ """
2211
+ EXAMPLES::
2212
+
2213
+ sage: g = line([(-1,1), (3,2)])
2214
+ sage: g.ymin()
2215
+ 1.0
2216
+ sage: g.ymin(-3)
2217
+ sage: g.ymin()
2218
+ -3.0
2219
+ """
2220
+ if ymin is None:
2221
+ return self.get_axes_range()['ymin']
2222
+ else:
2223
+ self.set_axes_range(ymin=ymin)
2224
+
2225
+ def ymax(self, ymax=None):
2226
+ """
2227
+ EXAMPLES::
2228
+
2229
+ sage: g = line([(-1,1), (3,2)])
2230
+ sage: g.ymax()
2231
+ 2.0
2232
+ sage: g.ymax(10)
2233
+ sage: g.ymax()
2234
+ 10.0
2235
+ """
2236
+ if ymax is None:
2237
+ return self.get_axes_range()['ymax']
2238
+ else:
2239
+ self.set_axes_range(ymax=ymax)
2240
+
2241
+ def get_minmax_data(self):
2242
+ r"""
2243
+ Return the x and y coordinate minimum and maximum.
2244
+
2245
+ .. warning::
2246
+
2247
+ The returned dictionary is mutable, but changing it does
2248
+ not change the xmin/xmax/ymin/ymax data. The minmax data is a function
2249
+ of the primitives which make up this Graphics object. To change the
2250
+ range of the axes, call methods :meth:`xmin`, :meth:`xmax`,
2251
+ :meth:`ymin`, :meth:`ymax`, or :meth:`set_axes_range`.
2252
+
2253
+ OUTPUT:
2254
+
2255
+ A dictionary whose keys give the xmin, xmax, ymin, and ymax
2256
+ data for this graphic.
2257
+
2258
+ EXAMPLES::
2259
+
2260
+ sage: g = line([(-1,1), (3,2)])
2261
+ sage: list(sorted(g.get_minmax_data().items()))
2262
+ [('xmax', 3.0), ('xmin', -1.0), ('ymax', 2.0), ('ymin', 1.0)]
2263
+
2264
+ Note that changing ymax doesn't change the output of get_minmax_data::
2265
+
2266
+ sage: g.ymax(10)
2267
+ sage: list(sorted(g.get_minmax_data().items()))
2268
+ [('xmax', 3.0), ('xmin', -1.0), ('ymax', 2.0), ('ymin', 1.0)]
2269
+
2270
+ The width/height ratio (in output units, after factoring in the
2271
+ chosen aspect ratio) of the plot is limited to `10^{-15}\dots
2272
+ 10^{15}`, otherwise floating point errors cause problems in
2273
+ matplotlib::
2274
+
2275
+ sage: l = line([(1e-19,-1), (-1e-19,+1)], aspect_ratio=1.0)
2276
+ sage: l.get_minmax_data()
2277
+ {'xmax': 1.00010000000000e-15,
2278
+ 'xmin': -9.99900000000000e-16,
2279
+ 'ymax': 1.0,
2280
+ 'ymin': -1.0}
2281
+ sage: l = line([(0,0), (1,1)], aspect_ratio=1e19)
2282
+ sage: l.get_minmax_data()
2283
+ {'xmax': 5000.50000000000, 'xmin': -4999.50000000000,
2284
+ 'ymax': 1.0, 'ymin': 0.0}
2285
+ """
2286
+ objects = self._objects
2287
+ if objects:
2288
+ minmax_data = [o.get_minmax_data() for o in objects]
2289
+ xmin = min(d['xmin'] for d in minmax_data)
2290
+ xmax = max(d['xmax'] for d in minmax_data)
2291
+ ymin = min(d['ymin'] for d in minmax_data)
2292
+ ymax = max(d['ymax'] for d in minmax_data)
2293
+ if isnan(xmin):
2294
+ xmin = 0
2295
+ sage.misc.verbose.verbose("xmin was NaN (setting to 0)", level=0)
2296
+ if isnan(xmax):
2297
+ xmax = 0
2298
+ sage.misc.verbose.verbose("xmax was NaN (setting to 0)", level=0)
2299
+ if isnan(ymin):
2300
+ ymin = 0
2301
+ sage.misc.verbose.verbose("ymin was NaN (setting to 0)", level=0)
2302
+ if isnan(ymax):
2303
+ ymax = 0
2304
+ sage.misc.verbose.verbose("ymax was NaN (setting to 0)", level=0)
2305
+ else:
2306
+ xmin = xmax = ymin = ymax = 0
2307
+
2308
+ if xmin == xmax:
2309
+ xmin -= 1
2310
+ xmax += 1
2311
+ if ymin == ymax:
2312
+ ymin -= 1
2313
+ ymax += 1
2314
+ return self._limit_output_aspect_ratio(xmin, xmax, ymin, ymax)
2315
+
2316
+ def _limit_output_aspect_ratio(self, xmin, xmax, ymin, ymax):
2317
+ r"""
2318
+ Private helper function for :meth:`get_minmax_data`.
2319
+
2320
+ INPUT:
2321
+
2322
+ - ``xmin``, ``xmax``, ``ymin``, ``ymax`` -- bounding box for
2323
+ the graphics
2324
+
2325
+ OUTPUT:
2326
+
2327
+ A dictionary whose keys give the xmin, xmax, ymin, and ymax
2328
+ data for this graphic. Possibly enlarged in order to keep the
2329
+ width/height ratio (in output units, after factoring in the
2330
+ chosen aspect ratio) of the plot is limited to `10^{-15}\dots
2331
+ 10^{15}` to avoid floating point issues in matplotlib.
2332
+
2333
+ EXAMPLES::
2334
+
2335
+ sage: l = line([(0,0), (1,1)], aspect_ratio=1.0)
2336
+ sage: l._limit_output_aspect_ratio(1, 2, 1e19, 3)
2337
+ {'xmax': -4999.50000000000,
2338
+ 'xmin': 5000.50000000000,
2339
+ 'ymax': 3,
2340
+ 'ymin': 1.00000000000000e19}
2341
+ sage: l._limit_output_aspect_ratio(1, 2, 3, 1e19)
2342
+ {'xmax': 5000.50000000000,
2343
+ 'xmin': -4999.50000000000,
2344
+ 'ymax': 1.00000000000000e19,
2345
+ 'ymin': 3}
2346
+ sage: l = line([(0,0), (1,1)], aspect_ratio=1e16)
2347
+ sage: l._limit_output_aspect_ratio(0, 1, 2, 3)
2348
+ {'xmax': 5.50000000000000, 'xmin': -4.50000000000000, 'ymax': 3, 'ymin': 2}
2349
+ """
2350
+ aspect_ratio = self.aspect_ratio()
2351
+ if aspect_ratio != 'automatic':
2352
+ width = xmax - xmin
2353
+ height = ymax - ymin
2354
+ output_aspect = abs(width / height / aspect_ratio)
2355
+ if output_aspect > 1e15:
2356
+ height = 1e15 * width / aspect_ratio
2357
+ ycenter = (ymax - ymin) / 2
2358
+ ymin = ycenter - height / 2
2359
+ ymax = ycenter + height / 2
2360
+ if output_aspect < 1e-15:
2361
+ width = 1e-15 * height * aspect_ratio
2362
+ xcenter = (xmax - xmin) / 2
2363
+ xmin = xcenter - width / 2
2364
+ xmax = xcenter + width / 2
2365
+ return {'xmin': xmin, 'xmax': xmax, 'ymin': ymin, 'ymax': ymax}
2366
+
2367
+ def _matplotlib_tick_formatter(self, subplot, base=(10, 10),
2368
+ locator_options={}, scale=('linear', 'linear'),
2369
+ tick_formatter=(None, None), ticks=(None, None),
2370
+ xmax=None, xmin=None, ymax=None, ymin=None):
2371
+ r"""
2372
+ Take a matplotlib subplot instance representing the graphic and set
2373
+ the ticks formatting. This function is only for internal use.
2374
+
2375
+ INPUT:
2376
+
2377
+ - ``subplot`` -- the subplot instance
2378
+
2379
+ EXAMPLES::
2380
+
2381
+ sage: # needs sage.symbolic
2382
+ sage: from matplotlib.figure import Figure
2383
+ sage: p = plot(x); d = p.get_minmax_data()
2384
+ sage: subplot = Figure().add_subplot(111)
2385
+ sage: p._objects[0]._render_on_subplot(subplot)
2386
+ sage: p._matplotlib_tick_formatter(subplot, **d)
2387
+ (<Axes...>,
2388
+ <matplotlib.ticker.MaxNLocator object at ...>,
2389
+ <matplotlib.ticker.MaxNLocator object at ...>,
2390
+ <matplotlib.ticker.ScalarFormatter object at ...>,
2391
+ <matplotlib.ticker.ScalarFormatter object at ...>)
2392
+ """
2393
+ # This function is created to refactor some code that is repeated
2394
+ # in the matplotlib function
2395
+ from matplotlib.ticker import (FixedLocator, Locator,
2396
+ LogFormatterMathtext,
2397
+ LogLocator, MaxNLocator,
2398
+ MultipleLocator,
2399
+ NullLocator, ScalarFormatter)
2400
+
2401
+ x_locator, y_locator = ticks
2402
+ # ---------------------- Location of x-ticks ---------------------
2403
+
2404
+ if x_locator is None:
2405
+ if scale[0] == 'log':
2406
+ x_locator = LogLocator(base=base[0])
2407
+ else:
2408
+ x_locator = MaxNLocator(**locator_options)
2409
+ elif isinstance(x_locator, Locator):
2410
+ pass
2411
+ elif x_locator == []:
2412
+ x_locator = NullLocator()
2413
+ elif isinstance(x_locator, list):
2414
+ x_locator = FixedLocator([float(x) for x in x_locator])
2415
+ else: # x_locator is a number which can be made a float
2416
+ from sage.functions.other import ceil, floor
2417
+ if floor(xmax / x_locator) - ceil(xmin / x_locator) > 1:
2418
+ x_locator = MultipleLocator(float(x_locator))
2419
+ else: # not enough room for two major ticks
2420
+ raise ValueError('Expand the range of the independent '
2421
+ 'variable to allow two multiples of your tick locator '
2422
+ '(option `ticks`).')
2423
+
2424
+ # ---------------------- Location of y-ticks ---------------------
2425
+ if y_locator is None:
2426
+ if scale[1] == 'log':
2427
+ y_locator = LogLocator(base=base[1])
2428
+ else:
2429
+ y_locator = MaxNLocator(**locator_options)
2430
+ elif isinstance(y_locator, Locator):
2431
+ pass
2432
+ elif y_locator == []:
2433
+ y_locator = NullLocator()
2434
+ elif isinstance(y_locator, list):
2435
+ y_locator = FixedLocator([float(y) for y in y_locator])
2436
+ else: # y_locator is a number which can be made a float
2437
+ from sage.functions.other import ceil, floor
2438
+ if floor(ymax / y_locator) - ceil(ymin / y_locator) > 1:
2439
+ y_locator = MultipleLocator(float(y_locator))
2440
+ else: # not enough room for two major ticks
2441
+ raise ValueError('Expand the range of the dependent '
2442
+ 'variable to allow two multiples of your tick locator '
2443
+ '(option `ticks`).')
2444
+
2445
+ x_formatter, y_formatter = tick_formatter
2446
+ from matplotlib.ticker import FuncFormatter, FixedFormatter
2447
+ from sage.misc.latex import latex
2448
+ from sage.structure.element import Expression
2449
+ from .misc import _multiple_of_constant
2450
+ # ---------------------- Formatting x-ticks ----------------------
2451
+ if x_formatter is None:
2452
+ if scale[0] == 'log':
2453
+ x_formatter = LogFormatterMathtext(base=base[0])
2454
+ else:
2455
+ x_formatter = ScalarFormatter()
2456
+ elif isinstance(x_formatter, Expression):
2457
+ x_const = x_formatter
2458
+ x_formatter = FuncFormatter(lambda n, pos:
2459
+ _multiple_of_constant(n, pos, x_const))
2460
+ elif x_formatter == "latex":
2461
+ if scale[0] == 'log':
2462
+ # We need to strip out '\\mathdefault' from the string
2463
+ x_formatter = FuncFormatter(lambda n, pos:
2464
+ LogFormatterMathtext(base=base[0])(n, pos).replace(
2465
+ "\\mathdefault", ""))
2466
+ else:
2467
+ # circumvent the problem of symbolic tick values (trac #34693)
2468
+ if isinstance(x_locator, FixedLocator):
2469
+ x_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[0]])
2470
+ else:
2471
+ x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n))
2472
+ elif isinstance(x_formatter, (list, tuple)):
2473
+ if (not isinstance(ticks[0], (list, tuple)) or
2474
+ len(ticks[0]) != len(x_formatter)):
2475
+ raise ValueError("If the first component of the list "
2476
+ "`tick_formatter` is a list then the first component "
2477
+ "of `ticks` must also be a list of equal length.")
2478
+ x_formatter = FixedFormatter(x_formatter)
2479
+ # ---------------------- Formatting y-ticks ----------------------
2480
+ if y_formatter is None:
2481
+ if scale[1] == 'log':
2482
+ y_formatter = LogFormatterMathtext(base=base[1])
2483
+ else:
2484
+ y_formatter = ScalarFormatter()
2485
+ elif isinstance(y_formatter, Expression):
2486
+ y_const = y_formatter
2487
+ y_formatter = FuncFormatter(lambda n, pos:
2488
+ _multiple_of_constant(n, pos, y_const))
2489
+ elif y_formatter == "latex":
2490
+ if scale[1] == 'log':
2491
+ # We need to strip out '\\mathdefault' from the string
2492
+ y_formatter = FuncFormatter(lambda n, pos:
2493
+ LogFormatterMathtext(base=base[1])(n, pos).replace(
2494
+ "\\mathdefault", ""))
2495
+ else:
2496
+ # circumvent the problem of symbolic tick values (trac #34693)
2497
+ if isinstance(y_locator, FixedLocator):
2498
+ y_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[1]])
2499
+ else:
2500
+ y_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n))
2501
+ elif isinstance(y_formatter, (list, tuple)):
2502
+ if (not isinstance(ticks[1], (list, tuple)) or
2503
+ len(ticks[1]) != len(y_formatter)):
2504
+ raise ValueError("If the second component of the list "
2505
+ "`tick_formatter` is a list then the second component "
2506
+ "of `ticks` must also be a list of equal length.")
2507
+ y_formatter = FixedFormatter(y_formatter)
2508
+
2509
+ subplot.xaxis.set_major_locator(x_locator)
2510
+ subplot.yaxis.set_major_locator(y_locator)
2511
+ subplot.xaxis.set_major_formatter(x_formatter)
2512
+ subplot.yaxis.set_major_formatter(y_formatter)
2513
+
2514
+ # Check for whether there will be too few ticks in the log scale case.
2515
+ # If there are not enough ticks (2 or more) to determine that the scale
2516
+ # is non-linear, we throw a warning.
2517
+ from warnings import warn
2518
+ tickwarnmsg = 'The %s-axis contains fewer than 2 ticks; '
2519
+ tickwarnmsg += 'the logarithmic scale of the plot may not be apparent '
2520
+ tickwarnmsg += 'to the reader.'
2521
+
2522
+ if (scale[0] == 'log' and not isinstance(x_locator, NullLocator) and
2523
+ len(subplot.xaxis.get_ticklocs()) < 2):
2524
+ warn(tickwarnmsg % 'x')
2525
+
2526
+ if (scale[1] == 'log' and not isinstance(y_locator, NullLocator) and
2527
+ len(subplot.yaxis.get_ticklocs()) < 2):
2528
+ warn(tickwarnmsg % 'y')
2529
+
2530
+ return (subplot, x_locator, y_locator, x_formatter, y_formatter)
2531
+
2532
+ def _get_vmin_vmax(self, vmin, vmax, basev, axes_pad):
2533
+ r"""
2534
+ Determine the min/max value for a variable plotted on a logarithmic
2535
+ scale. The motivation is that we desire at least two ticks for a log
2536
+ plot; otherwise the reader may assume that the scale is linear. For
2537
+ internal use only.
2538
+
2539
+ We check if this case occurs (for e.g. assuming xmin < xmax)::
2540
+
2541
+ floor(logxmin) ceil(logxmax)
2542
+ ----|---------+----------+----------|----------------------|--
2543
+ logxmin logxmax
2544
+
2545
+ Or if this case occurs (assuming xmin < xmax)::
2546
+
2547
+ floor(logxmin) floor(logxmax) ceil(logxmax)
2548
+ ----|---------+---------------------|-----+----------------|--
2549
+ logxmin logxmax
2550
+
2551
+
2552
+ INPUT:
2553
+
2554
+ - ``vmin`` -- the current min for this variable (e.g. xmin or ymin)
2555
+
2556
+ - ``vmax`` -- the current max for this variable (e.g. xmax or ymax)
2557
+
2558
+ - ``basev`` -- the base of the logarithmic scale for this variable
2559
+
2560
+ - ``axes_pad`` -- the padding for the axis. It determines the
2561
+ exponent of the fraction of the minimum (resp. maximum) that is
2562
+ subtracted from the minimum (resp. added to the maximum) value of
2563
+ the axis. For instance if the minimum is `m` and the base of the
2564
+ axis is `b` then the new minimum after padding the axis will be
2565
+ `m - m/b^{\mathrm{axes\_pad}}`.
2566
+
2567
+ OUTPUT:
2568
+
2569
+ A new (min,max) pair for this variable, suitable for its logarithmic
2570
+ scale.
2571
+
2572
+ EXAMPLES:
2573
+
2574
+ On a base-10 logarithmic scale, we should have ``vmin``/``vmax``
2575
+ at least 10 units apart::
2576
+
2577
+ sage: p = Graphics()
2578
+ sage: p._get_vmin_vmax(1, 2, 10, None) == (9/10, 10)
2579
+ True
2580
+ sage: p._get_vmin_vmax(1, 5, 10, None) == (9/10, 10)
2581
+ True
2582
+ sage: p._get_vmin_vmax(1, 10, 10, None)
2583
+ (9/10, 11)
2584
+ sage: p._get_vmin_vmax(1, 11, 10, None)
2585
+ (9/10, 121/10)
2586
+ sage: p._get_vmin_vmax(1, 50, 10, None)
2587
+ (9/10, 55)
2588
+
2589
+ We can set the ``axes_pad`` separately::
2590
+
2591
+ sage: p._get_vmin_vmax(1, 50, 2, 2)
2592
+ (0.75, 62.5)
2593
+
2594
+ Nonpositive values of ``vmin`` are not accepted due to the domain
2595
+ of the logarithm function::
2596
+
2597
+ sage: p = Graphics()
2598
+ sage: p._get_vmin_vmax(-1,2,10, None)
2599
+ Traceback (most recent call last):
2600
+ ...
2601
+ ValueError: vmin must be positive
2602
+
2603
+ And ``vmax`` must be greater than ``vmin``::
2604
+
2605
+ sage: p._get_vmin_vmax(1,-2,10, None)
2606
+ Traceback (most recent call last):
2607
+ ...
2608
+ ValueError: vmin must be less than vmax
2609
+ """
2610
+ if vmin <= 0:
2611
+ raise ValueError('vmin must be positive')
2612
+
2613
+ if vmin >= vmax:
2614
+ raise ValueError('vmin must be less than vmax')
2615
+
2616
+ import math
2617
+ if axes_pad is None:
2618
+ axes_pad = 1
2619
+ else:
2620
+ axes_pad = float(abs(axes_pad))
2621
+
2622
+ logvmin = math.log(vmin) / math.log(basev)
2623
+ logvmax = math.log(vmax) / math.log(basev)
2624
+
2625
+ if math.floor(logvmax) - math.ceil(logvmin) < 0:
2626
+ vmax = basev**math.ceil(logvmax)
2627
+ vmin = basev**math.floor(logvmin)
2628
+ elif math.floor(logvmax) - math.ceil(logvmin) < 1:
2629
+ if logvmax - math.floor(logvmax) > math.ceil(logvmin) - logvmin:
2630
+ vmax = basev**math.ceil(logvmax)
2631
+ if axes_pad > 0:
2632
+ vmin -= vmin * basev**(-axes_pad)
2633
+ else:
2634
+ vmin = basev**math.floor(logvmin)
2635
+ if axes_pad > 0:
2636
+ vmax += vmax * basev**(-axes_pad)
2637
+ elif axes_pad > 0:
2638
+ # pad the axes if we haven't expanded the axes earlier.
2639
+ vmin -= vmin * basev**(-axes_pad)
2640
+ vmax += vmax * basev**(-axes_pad)
2641
+
2642
+ return vmin, vmax
2643
+
2644
+ def matplotlib(self, filename=None,
2645
+ xmin=None, xmax=None, ymin=None, ymax=None,
2646
+ figsize=None, figure=None, sub=None,
2647
+ axes=None, axes_labels=None, axes_labels_size=None,
2648
+ flip_x=False, flip_y=False,
2649
+ fontsize=None, frame=False, verify=True,
2650
+ aspect_ratio=None,
2651
+ gridlines=None, gridlinesstyle=None,
2652
+ vgridlinesstyle=None, hgridlinesstyle=None,
2653
+ show_legend=None, legend_options=None,
2654
+ axes_pad=None, ticks_integer=None,
2655
+ tick_formatter=None, ticks=None, title=None,
2656
+ title_pos=None, base=None, scale=None,
2657
+ stylesheet=None,
2658
+ typeset='default'):
2659
+ r"""
2660
+ Construct or modify a Matplotlib figure by drawing ``self`` on it.
2661
+
2662
+ INPUT (partial description, involving only Matplotlib objects; see
2663
+ :meth:`show` for the other arguments):
2664
+
2665
+ - ``figure`` -- (default: ``None``) Matplotlib figure (class
2666
+ ``matplotlib.figure.Figure``) on which ``self`` is to be displayed;
2667
+ if ``None``, the figure will be created from the parameter
2668
+ ``figsize``
2669
+
2670
+ - ``figsize`` -- (default: ``None``) width or [width, height] in inches
2671
+ of the Matplotlib figure in case ``figure`` is ``None``; if
2672
+ ``figsize`` is ``None``, Matplotlib's default (6.4 x 4.8 inches) is
2673
+ used
2674
+
2675
+ - ``sub`` -- (default: ``None``) subpart of the figure, as an
2676
+ instance of Matplotlib "axes" (class ``matplotlib.axes.Axes``) on
2677
+ which ``self`` is to be drawn; if ``None``, the subpart will be
2678
+ created so as to cover the whole figure
2679
+
2680
+ OUTPUT:
2681
+
2682
+ - a ``matplotlib.figure.Figure`` object; if the argument ``figure`` is
2683
+ provided, this is the same object as ``figure``.
2684
+
2685
+ EXAMPLES::
2686
+
2687
+ sage: c = circle((1,1),1)
2688
+ sage: print(c.matplotlib())
2689
+ Figure(640x480)
2690
+
2691
+ To obtain the first Matplotlib ``Axes`` object inside of the
2692
+ figure, you can do something like the following.
2693
+
2694
+ ::
2695
+
2696
+ sage: p = plot(sin(x), (x, -2*pi, 2*pi)) # needs sage.symbolic
2697
+ sage: figure = p.matplotlib() # needs sage.symbolic
2698
+ sage: axes = figure.axes[0] # needs sage.symbolic
2699
+
2700
+ TESTS:
2701
+
2702
+ We verify that :issue:`10291` is fixed::
2703
+
2704
+ sage: # needs sage.symbolic
2705
+ sage: p = plot(sin(x), (x, -2*pi, 2*pi))
2706
+ sage: figure = p.matplotlib()
2707
+ sage: axes_range = p.get_axes_range()
2708
+ sage: figure = p.matplotlib()
2709
+ sage: axes_range2 = p.get_axes_range()
2710
+ sage: axes_range == axes_range2
2711
+ True
2712
+
2713
+ We verify that legend options are properly handled (:issue:`12960`).
2714
+ First, we test with no options, and next with an incomplete set of
2715
+ options.::
2716
+
2717
+ sage: # needs sage.symbolic
2718
+ sage: p = plot(x, legend_label='aha')
2719
+ sage: p.legend(True)
2720
+ sage: pm = p.matplotlib()
2721
+ sage: pm = p.matplotlib(legend_options={'font_size': 'small'})
2722
+
2723
+ The title should not overlap with the axes labels nor the frame in
2724
+ the following plot (see :issue:`10512`)::
2725
+
2726
+ sage: plot(sin(x^2), (x, -3, 3), title='Plot of sin(x^2)', # needs sage.symbolic
2727
+ ....: axes_labels=['x','y'], frame=True)
2728
+ Graphics object consisting of 1 graphics primitive
2729
+
2730
+ ``typeset`` must not be set to an arbitrary string::
2731
+
2732
+ sage: plot(x, typeset='garbage') # needs sage.symbolic
2733
+ doctest:...: ...RichReprWarning: Exception in _rich_repr_ while
2734
+ displaying object: typeset must be set to one of 'default',
2735
+ 'latex', or 'type1'; got 'garbage'.
2736
+ Graphics object consisting of 1 graphics primitive
2737
+
2738
+ We verify that numerical options are changed to float before saving (:issue:`14741`).
2739
+ By default, Sage 5.10 changes float objects to the `RealLiteral` type.
2740
+ The patch changes them to float before creating `matplotlib` objects.::
2741
+
2742
+ sage: # long time, needs sage.symbolic
2743
+ sage: f = lambda x, y: abs(cos((x + I * y) ** 4)) - 1
2744
+ sage: g = implicit_plot(f, (-4, 4), (-3, 3), linewidth=0.6)
2745
+ sage: gm = g.matplotlib()
2746
+
2747
+ If the axes are flipped, the limits of the axes get swapped::
2748
+
2749
+ sage: # needs sage.symbolic
2750
+ sage: p = plot(2*x, 1, 2)
2751
+ sage: sub, = p.matplotlib(flip_y=True, flip_x=True).axes
2752
+ sage: xmin, xmax = sub.get_xlim()
2753
+ sage: ymin, ymax = sub.get_ylim()
2754
+ sage: xmin > xmax, ymin > ymax
2755
+ (...True..., ...True...)
2756
+ """
2757
+ if not isinstance(ticks, (list, tuple)):
2758
+ ticks = (ticks, None)
2759
+ if legend_options is None:
2760
+ legend_options = {}
2761
+ # as discussed in trac #25799 and #23696, Sage prefers the computer
2762
+ # modern fonts of TeX for math texts such as axes labels, but otherwise
2763
+ # adopts the default style of matplotlib
2764
+ from matplotlib import rcParams
2765
+ rcParams['mathtext.fontset'] = 'cm'
2766
+ rcParams['mathtext.rm'] = 'serif'
2767
+
2768
+ import matplotlib.pyplot as plt
2769
+ if stylesheet in plt.style.available:
2770
+ plt.style.use(stylesheet)
2771
+
2772
+ from sage.structure.element import Expression
2773
+ # make sure both formatters typeset or both don't
2774
+ if not isinstance(tick_formatter, (list, tuple)):
2775
+ if tick_formatter == "latex" or isinstance(tick_formatter, Expression):
2776
+ tick_formatter = (tick_formatter, "latex")
2777
+ else:
2778
+ tick_formatter = (tick_formatter, None)
2779
+
2780
+ global do_verify
2781
+ do_verify = verify
2782
+
2783
+ if axes is None:
2784
+ axes = self._show_axes
2785
+
2786
+ from matplotlib.figure import Figure
2787
+ if typeset == 'type1': # Requires LaTeX, dvipng, gs to be installed.
2788
+ rcParams['ps.useafm'] = True
2789
+ rcParams['pdf.use14corefonts'] = True
2790
+ rcParams['text.usetex'] = True
2791
+ elif typeset == 'latex': # Requires LaTeX, dvipng, gs to be installed.
2792
+ rcParams['ps.useafm'] = False
2793
+ rcParams['pdf.use14corefonts'] = False
2794
+ rcParams['text.usetex'] = True
2795
+ elif typeset != 'default': # We won't change (maybe user-set) defaults
2796
+ raise ValueError("typeset must be set to one of 'default', 'latex',"
2797
+ f" or 'type1'; got '{typeset}'.")
2798
+
2799
+ self.fontsize(fontsize)
2800
+ self.axes_labels(l=axes_labels)
2801
+ self.axes_labels_size(s=axes_labels_size)
2802
+
2803
+ # If no matplotlib figure is provided, it is created here:
2804
+ if figure is None:
2805
+ if figsize is not None:
2806
+ figsize = _parse_figsize(figsize)
2807
+ figure = Figure(figsize=figsize)
2808
+
2809
+ # The incoming subplot instance
2810
+ subplot = sub
2811
+ if not subplot:
2812
+ subplot = figure.add_subplot(111)
2813
+ # Add all the primitives to the subplot
2814
+ old_opts = {}
2815
+ for g in self._objects:
2816
+ opts, old_opts[g] = g.options(), g.options()
2817
+ for k, v in opts.items():
2818
+ try:
2819
+ if v.parent() in sage.categories.fields.Fields():
2820
+ opts[k] = float(v)
2821
+ except (AttributeError, TypeError):
2822
+ pass
2823
+ g.set_options(opts)
2824
+ g._render_on_subplot(subplot)
2825
+ if hasattr(g, '_bbox_extra_artists'):
2826
+ self._bbox_extra_artists.extend(g._bbox_extra_artists)
2827
+ # Set the aspect ratio
2828
+ if aspect_ratio is None:
2829
+ aspect_ratio = self.aspect_ratio()
2830
+ if aspect_ratio == 'automatic':
2831
+ subplot.set_aspect('auto', adjustable='box')
2832
+ else:
2833
+ subplot.set_aspect(aspect_ratio, adjustable='box')
2834
+
2835
+ # ---------------- Set the axes limits and scale ------------------
2836
+ self.set_axes_range(xmin, xmax, ymin, ymax)
2837
+ d = self.get_axes_range()
2838
+ xmin = d['xmax' if flip_x else 'xmin']
2839
+ xmax = d['xmin' if flip_x else 'xmax']
2840
+ ymin = d['ymax' if flip_y else 'ymin']
2841
+ ymax = d['ymin' if flip_y else 'ymax']
2842
+
2843
+ xscale, yscale, basex, basey = self._set_scale(subplot, scale=scale,
2844
+ base=base)
2845
+
2846
+ # If any of the x-data are negative, we leave the min/max alone.
2847
+ if xscale == 'log' and min(xmin, xmax) > 0:
2848
+ if xmin < xmax:
2849
+ xmin, xmax = self._get_vmin_vmax(xmin, xmax, basex, axes_pad)
2850
+ else:
2851
+ xmax, xmin = self._get_vmin_vmax(xmax, xmin, basex, axes_pad)
2852
+ else:
2853
+ xpad = 0.02 if axes_pad is None else axes_pad
2854
+ xpad = (xmax - xmin) * float(xpad)
2855
+ xmax += xpad
2856
+ xmin -= xpad
2857
+
2858
+ # Likewise for the y-data.
2859
+ if yscale == 'log' and min(ymin, ymax) > 0:
2860
+ if ymin < ymax:
2861
+ ymin, ymax = self._get_vmin_vmax(ymin, ymax, basey, axes_pad)
2862
+ else:
2863
+ ymax, ymin = self._get_vmin_vmax(ymax, ymin, basey, axes_pad)
2864
+ else:
2865
+ ypad = 0.02 if axes_pad is None else axes_pad
2866
+ ypad = (ymax - ymin) * float(ypad)
2867
+ ymax += ypad
2868
+ ymin -= ypad
2869
+
2870
+ # -------------------------- Set the legend -----------------------
2871
+ if show_legend is None:
2872
+ show_legend = self._show_legend
2873
+
2874
+ if show_legend:
2875
+ from matplotlib.font_manager import FontProperties
2876
+ lopts = {}
2877
+ lopts.update(legend_options)
2878
+ lopts.update(self._legend_opts)
2879
+ prop = FontProperties(
2880
+ family=lopts.pop('font_family', 'sans-serif'),
2881
+ size=lopts.pop('font_size', 'medium'),
2882
+ style=lopts.pop('font_style', 'normal'),
2883
+ weight=lopts.pop('font_weight', 'medium'),
2884
+ variant=lopts.pop('font_variant', 'normal'))
2885
+ color = lopts.pop('back_color', 'white')
2886
+ if 'loc' in lopts:
2887
+ loc = lopts['loc']
2888
+ if isinstance(loc, Integral):
2889
+ # matplotlib 3.8 doesn't support sage integers
2890
+ lopts['loc'] = int(loc)
2891
+ leg = subplot.legend(prop=prop, **lopts)
2892
+ if leg is None:
2893
+ from warnings import warn
2894
+ warn("legend requested but no items are labeled")
2895
+ else:
2896
+ # color
2897
+ lframe = leg.get_frame()
2898
+ lframe.set_facecolor(color)
2899
+ from sage.plot.colors import to_mpl_color
2900
+ for txt, color in zip(leg.get_texts(), self._legend_colors):
2901
+ if color is not None:
2902
+ txt.set_color(to_mpl_color(color))
2903
+
2904
+ subplot.set_xlim([xmin, xmax])
2905
+ subplot.set_ylim([ymin, ymax])
2906
+
2907
+ locator_options = {'nbins': 9, 'steps': [1, 2, 5, 10],
2908
+ 'integer': ticks_integer}
2909
+
2910
+ if axes is None:
2911
+ axes = self._show_axes
2912
+
2913
+ for spine in subplot.spines.values():
2914
+ spine.set_color(self._axes_color)
2915
+ spine.set_linewidth(self._axes_width)
2916
+
2917
+ if frame:
2918
+ # For now, set the formatter to the old one, since that is
2919
+ # sort of what we are used to. We should eventually look at
2920
+ # the default one to see if we like it better.
2921
+
2922
+ (subplot, x_locator, y_locator,
2923
+ x_formatter, y_formatter) = self._matplotlib_tick_formatter(
2924
+ subplot, base=(basex, basey),
2925
+ locator_options=locator_options,
2926
+ scale=(xscale, yscale),
2927
+ tick_formatter=tick_formatter, ticks=ticks,
2928
+ xmax=xmax, xmin=xmin, ymax=ymax, ymin=ymin)
2929
+
2930
+ subplot.set_frame_on(True)
2931
+ if axes and xscale == 'linear' and yscale == 'linear':
2932
+ if (ymin <= 0 and ymax >= 0) or (ymax <= 0 and ymin >= 0):
2933
+ subplot.axhline(color=self._axes_color,
2934
+ linewidth=self._axes_width)
2935
+ if (xmin <= 0 and xmax >= 0) or (xmax <= 0 and xmin >= 0):
2936
+ subplot.axvline(color=self._axes_color,
2937
+ linewidth=self._axes_width)
2938
+
2939
+ elif axes:
2940
+ ymiddle = False
2941
+ xmiddle = False
2942
+ # Note that the user may specify a custom xmin and xmax which
2943
+ # flips the axis horizontally. Hence we need to check for both
2944
+ # the possibilities in the if statements below. Similar
2945
+ # comments hold for ymin and ymax.
2946
+ if xscale == 'log':
2947
+ if xmax > xmin:
2948
+ subplot.spines['right'].set_visible(False)
2949
+ subplot.spines['left'].set_position(('outward', 10))
2950
+ subplot.yaxis.set_ticks_position('left')
2951
+ subplot.yaxis.set_label_position('left')
2952
+ yaxis = 'left'
2953
+ elif xmax < xmin:
2954
+ subplot.spines['left'].set_visible(False)
2955
+ subplot.spines['right'].set_position(('outward', 10))
2956
+ subplot.yaxis.set_ticks_position('right')
2957
+ subplot.yaxis.set_label_position('right')
2958
+ yaxis = 'right'
2959
+ elif (xmin > 0 and xmax > xmin) or (xmax > 0 and xmin > xmax):
2960
+ subplot.spines['right'].set_visible(False)
2961
+ subplot.spines['left'].set_position(('outward', 10))
2962
+ subplot.yaxis.set_ticks_position('left')
2963
+ subplot.yaxis.set_label_position('left')
2964
+ yaxis = 'left'
2965
+ elif (xmax < 0 and xmax > xmin) or (xmin < 0 and xmin > xmax):
2966
+ subplot.spines['left'].set_visible(False)
2967
+ subplot.spines['right'].set_position(('outward', 10))
2968
+ subplot.yaxis.set_ticks_position('right')
2969
+ subplot.yaxis.set_label_position('right')
2970
+ yaxis = 'right'
2971
+ else:
2972
+ subplot.spines['left'].set_position('zero')
2973
+ subplot.yaxis.set_ticks_position('left')
2974
+ subplot.yaxis.set_label_position('left')
2975
+ subplot.spines['right'].set_visible(False)
2976
+ ymiddle = True
2977
+ yaxis = 'left'
2978
+
2979
+ if yscale == 'log':
2980
+ if ymax > ymin:
2981
+ subplot.spines['top'].set_visible(False)
2982
+ subplot.spines['bottom'].set_position(('outward', 10))
2983
+ subplot.xaxis.set_ticks_position('bottom')
2984
+ subplot.xaxis.set_label_position('bottom')
2985
+ xaxis = 'bottom'
2986
+ elif ymax < ymin:
2987
+ subplot.spines['bottom'].set_visible(False)
2988
+ subplot.spines['top'].set_position(('outward', 10))
2989
+ subplot.xaxis.set_ticks_position('top')
2990
+ subplot.xaxis.set_label_position('top')
2991
+ xaxis = 'top'
2992
+ elif (ymin > 0 and ymax > ymin) or (ymax > 0 and ymin > ymax):
2993
+ subplot.spines['top'].set_visible(False)
2994
+ subplot.spines['bottom'].set_position(('outward', 10))
2995
+ subplot.xaxis.set_ticks_position('bottom')
2996
+ subplot.xaxis.set_label_position('bottom')
2997
+ xaxis = 'bottom'
2998
+ elif (ymax < 0 and ymax > ymin) or (ymin < 0 and ymin > ymax):
2999
+ subplot.spines['bottom'].set_visible(False)
3000
+ subplot.spines['top'].set_position(('outward', 10))
3001
+ subplot.xaxis.set_ticks_position('top')
3002
+ subplot.xaxis.set_label_position('top')
3003
+ xaxis = 'top'
3004
+ else:
3005
+ subplot.spines['bottom'].set_position('zero')
3006
+ subplot.xaxis.set_ticks_position('bottom')
3007
+ subplot.xaxis.set_label_position('bottom')
3008
+ subplot.spines['top'].set_visible(False)
3009
+ xmiddle = True
3010
+ xaxis = 'bottom'
3011
+
3012
+ # For now, set the formatter to the old one, since that is
3013
+ # sort of what we are used to. We should eventually look at
3014
+ # the default one to see if we like it better.
3015
+
3016
+ (subplot, x_locator, y_locator,
3017
+ x_formatter, y_formatter) = self._matplotlib_tick_formatter(
3018
+ subplot, base=(basex, basey),
3019
+ locator_options=locator_options,
3020
+ scale=(xscale, yscale),
3021
+ tick_formatter=tick_formatter, ticks=ticks,
3022
+ xmax=xmax, xmin=xmin, ymax=ymax, ymin=ymin)
3023
+
3024
+ # Make ticklines go on both sides of the axes
3025
+ # if xmiddle:
3026
+ # for t in subplot.xaxis.get_majorticklines():
3027
+ # t.set_marker("|")
3028
+ # t.set_markersize(8)
3029
+ # for t in subplot.xaxis.get_minorticklines():
3030
+ # t.set_marker("|")
3031
+ # t.set_markersize(4)
3032
+
3033
+ # if ymiddle:
3034
+ # for t in subplot.yaxis.get_majorticklines():
3035
+ # t.set_marker("|")
3036
+ # t.set_markersize(8)
3037
+ # for t in subplot.yaxis.get_minorticklines():
3038
+ # t.set_marker("|")
3039
+ # t.set_markersize(4)
3040
+
3041
+ # Make the zero tick labels disappear if the axes cross
3042
+ # inside the picture, but only if log scale is not used
3043
+ if (xmiddle and ymiddle and xscale == 'linear' == yscale):
3044
+ from sage.plot.plot import SelectiveFormatter
3045
+ subplot.yaxis.set_major_formatter(SelectiveFormatter(
3046
+ subplot.yaxis.get_major_formatter(), skip_values=[0]))
3047
+ subplot.xaxis.set_major_formatter(SelectiveFormatter(
3048
+ subplot.xaxis.get_major_formatter(), skip_values=[0]))
3049
+
3050
+ else:
3051
+ for spine in subplot.spines.values():
3052
+ spine.set_visible(False)
3053
+ from matplotlib.ticker import NullFormatter, NullLocator
3054
+ subplot.xaxis.set_major_formatter(NullFormatter())
3055
+ subplot.yaxis.set_major_formatter(NullFormatter())
3056
+ subplot.xaxis.set_major_locator(NullLocator())
3057
+ subplot.yaxis.set_major_locator(NullLocator())
3058
+
3059
+ if frame or axes:
3060
+ # Make minor tickmarks, unless we specify fixed ticks or no ticks
3061
+ # We do this change only on linear scale, otherwise matplotlib
3062
+ # errors out with a memory error.
3063
+ from matplotlib.ticker import (AutoMinorLocator, FixedLocator,
3064
+ LogLocator, NullLocator)
3065
+ if isinstance(x_locator, (NullLocator, FixedLocator)):
3066
+ subplot.xaxis.set_minor_locator(NullLocator())
3067
+ elif xscale == 'linear':
3068
+ subplot.xaxis.set_minor_locator(AutoMinorLocator())
3069
+ else: # log scale
3070
+ from sage.arith.srange import srange
3071
+ base_inv = 1.0 / basex
3072
+ subs = [float(_) for _ in srange(2 * base_inv, 1, base_inv)]
3073
+ subplot.xaxis.set_minor_locator(LogLocator(base=basex,
3074
+ subs=subs))
3075
+ if isinstance(y_locator, (NullLocator, FixedLocator)):
3076
+ subplot.yaxis.set_minor_locator(NullLocator())
3077
+ elif yscale == 'linear':
3078
+ subplot.yaxis.set_minor_locator(AutoMinorLocator())
3079
+ else: # log scale
3080
+ from sage.arith.srange import srange
3081
+ base_inv = 1.0 / basey
3082
+ subs = [float(_) for _ in srange(2 * base_inv, 1, base_inv)]
3083
+ subplot.yaxis.set_minor_locator(LogLocator(base=basey,
3084
+ subs=subs))
3085
+ # Set the color and fontsize of ticks
3086
+ subplot.tick_params(color=self._axes_color,
3087
+ labelcolor=self._tick_label_color,
3088
+ labelsize=self._fontsize, which='both')
3089
+
3090
+ if gridlines is not None:
3091
+ if isinstance(gridlines, (list, tuple)):
3092
+ vgridlines, hgridlines = gridlines
3093
+ else:
3094
+ hgridlines = gridlines
3095
+ vgridlines = gridlines
3096
+
3097
+ if gridlinesstyle is None:
3098
+ # Set up the default grid style
3099
+ gridlinesstyle = {'color': 'black', 'linestyle': ':',
3100
+ 'linewidth': 0.5}
3101
+
3102
+ vgridstyle = gridlinesstyle.copy()
3103
+ if vgridlinesstyle is not None:
3104
+ vgridstyle.update(vgridlinesstyle)
3105
+
3106
+ hgridstyle = gridlinesstyle.copy()
3107
+ if hgridlinesstyle is not None:
3108
+ hgridstyle.update(hgridlinesstyle)
3109
+
3110
+ if hgridlines == 'minor':
3111
+ hgridstyle['which'] = 'both'
3112
+ if vgridlines == 'minor':
3113
+ vgridstyle['which'] = 'both'
3114
+
3115
+ if not isinstance(hgridlines, str) and isinstance(hgridlines, Iterable):
3116
+ hlines = iter(hgridlines)
3117
+ hgridstyle.pop("minor", None)
3118
+ for hline in hlines:
3119
+ if isinstance(hline, (list, tuple)):
3120
+ hl, style = hline
3121
+ st = hgridstyle.copy()
3122
+ st.update(style)
3123
+ else:
3124
+ hl = hline
3125
+ st = hgridstyle
3126
+ subplot.axhline(hl, **st)
3127
+ else:
3128
+ if hgridlines not in (None, False):
3129
+ subplot.yaxis.grid(True, **hgridstyle)
3130
+
3131
+ if not isinstance(vgridlines, str) and isinstance(vgridlines, Iterable):
3132
+ vlines = iter(vgridlines)
3133
+ vgridstyle.pop("minor", None)
3134
+ for vline in vlines:
3135
+ if isinstance(vline, (list, tuple)):
3136
+ vl, style = vline
3137
+ st = vgridstyle.copy()
3138
+ st.update(style)
3139
+ else:
3140
+ vl = vline
3141
+ st = vgridstyle
3142
+ subplot.axvline(vl, **st)
3143
+ else:
3144
+ if vgridlines not in (None, False):
3145
+ subplot.xaxis.grid(True, **vgridstyle)
3146
+
3147
+ if self._axes_labels is not None:
3148
+ label_options = {}
3149
+ label_options['color'] = self._axes_label_color
3150
+ label_options['size'] = int(self._axes_labels_size * self._fontsize)
3151
+ subplot.set_xlabel(self._axes_labels[0], **label_options)
3152
+ subplot.set_ylabel(self._axes_labels[1], **label_options)
3153
+
3154
+ if axes is True and frame is False:
3155
+ # We set the label positions according to where we are
3156
+ # drawing the axes.
3157
+ if xaxis == 'bottom':
3158
+ yaxis_labely = subplot.get_ylim()[1]
3159
+ yaxis_labeloffset = 8
3160
+ yaxis_vert = 'bottom'
3161
+ xaxis_labely = 0
3162
+ xaxis_vert = 'baseline'
3163
+ else:
3164
+ yaxis_labely = subplot.get_ylim()[0]
3165
+ yaxis_labeloffset = -8
3166
+ yaxis_vert = 'top'
3167
+ xaxis_labely = 1
3168
+ xaxis_vert = 'top'
3169
+
3170
+ if yaxis == 'left':
3171
+ xaxis_labelx = subplot.get_xlim()[1]
3172
+ xaxis_labeloffset = 8
3173
+ xaxis_horiz = 'left'
3174
+ yaxis_labelx = 0
3175
+ else:
3176
+ xaxis_labelx = subplot.get_xlim()[0]
3177
+ xaxis_labeloffset = -8
3178
+ xaxis_horiz = 'right'
3179
+ yaxis_labelx = 1
3180
+
3181
+ from matplotlib.transforms import offset_copy
3182
+ xlabel = subplot.xaxis.get_label()
3183
+ xlabel.set_horizontalalignment(xaxis_horiz)
3184
+ xlabel.set_verticalalignment(xaxis_vert)
3185
+ trans = subplot.spines[xaxis].get_transform()
3186
+ labeltrans = offset_copy(trans, figure, x=xaxis_labeloffset,
3187
+ y=0, units='points')
3188
+ subplot.xaxis.set_label_coords(x=xaxis_labelx,
3189
+ y=xaxis_labely, transform=labeltrans)
3190
+
3191
+ ylabel = subplot.yaxis.get_label()
3192
+ ylabel.set_horizontalalignment('center')
3193
+ ylabel.set_verticalalignment(yaxis_vert)
3194
+ ylabel.set_rotation('horizontal')
3195
+ trans = subplot.spines[yaxis].get_transform()
3196
+ labeltrans = offset_copy(trans, figure, x=0,
3197
+ y=yaxis_labeloffset, units='points')
3198
+ subplot.yaxis.set_label_coords(x=yaxis_labelx,
3199
+ y=yaxis_labely, transform=labeltrans)
3200
+
3201
+ # This option makes the xlim and ylim limits not take effect
3202
+ # todo: figure out which limits were specified, and let the
3203
+ # free limits autoscale
3204
+ # subplot.autoscale_view(tight=True)
3205
+ if title is not None:
3206
+ if title_pos is not None:
3207
+ if (not isinstance(title_pos, (list, tuple)) or
3208
+ len(title_pos) != 2):
3209
+ raise ValueError("'title_pos' must be a list or tuple "
3210
+ "of two real numbers.")
3211
+ title_pos = (float(title_pos[0]), float(title_pos[1]))
3212
+
3213
+ if (frame) or (axes_labels is None):
3214
+ if title_pos is not None:
3215
+ subplot.set_title(title, fontsize=fontsize,
3216
+ position=title_pos)
3217
+ else:
3218
+ subplot.set_title(title, fontsize=fontsize)
3219
+ else:
3220
+ # frame is false axes is not None, and neither is axes_labels
3221
+ # Then, the title is moved up to avoid overlap with axes labels
3222
+ if title_pos is None:
3223
+ title_pos = (0.5, 1.05)
3224
+ subplot.set_title(title, fontsize=fontsize, position=title_pos)
3225
+
3226
+ for g in self._objects:
3227
+ g.set_options(old_opts[g])
3228
+
3229
+ return figure
3230
+
3231
+ def save_image(self, filename=None, *args, **kwds):
3232
+ r"""
3233
+ Save an image representation of ``self``.
3234
+
3235
+ The image type is determined by the extension of the filename.
3236
+ For example, this could be ``.png``, ``.jpg``, ``.gif``,
3237
+ ``.pdf``, ``.svg``. Currently this is implemented by calling
3238
+ the :meth:`save` method of self, passing along all arguments
3239
+ and keywords.
3240
+
3241
+ .. NOTE::
3242
+
3243
+ Not all image types are necessarily implemented for all
3244
+ graphics types. See :meth:`save` for more details.
3245
+
3246
+ EXAMPLES::
3247
+
3248
+ sage: import tempfile
3249
+ sage: c = circle((1,1), 1, color='red')
3250
+ sage: with tempfile.NamedTemporaryFile(suffix='.png') as f:
3251
+ ....: c.save_image(f.name, xmin=-1, xmax=3,
3252
+ ....: ymin=-1, ymax=3)
3253
+ """
3254
+ self.save(filename, *args, **kwds)
3255
+
3256
+ # filename argument is written explicitly so that it can be used as a
3257
+ # positional one, which is a very likely usage for this function.
3258
+
3259
+ @suboptions('legend', **LEGEND_OPTIONS)
3260
+ def save(self, filename, **kwds):
3261
+ r"""
3262
+ Save the graphics to an image file.
3263
+
3264
+ INPUT:
3265
+
3266
+ - ``filename`` -- string. The filename and the image format
3267
+ given by the extension, which can be one of the following:
3268
+
3269
+ * ``.eps``,
3270
+
3271
+ * ``.pdf``,
3272
+
3273
+ * ``.pgf``,
3274
+
3275
+ * ``.png``,
3276
+
3277
+ * ``.ps``,
3278
+
3279
+ * ``.sobj`` (for a Sage object you can load later),
3280
+
3281
+ * ``.svg``,
3282
+
3283
+ * empty extension will be treated as ``.sobj``.
3284
+
3285
+ All other keyword arguments will be passed to the plotter.
3286
+
3287
+ OUTPUT: none
3288
+
3289
+ EXAMPLES::
3290
+
3291
+ sage: c = circle((1,1), 1, color='red')
3292
+ sage: from tempfile import NamedTemporaryFile
3293
+ sage: with NamedTemporaryFile(suffix='.png') as f:
3294
+ ....: c.save(f.name, xmin=-1, xmax=3, ymin=-1, ymax=3)
3295
+
3296
+ To make a figure bigger or smaller, use ``figsize``::
3297
+
3298
+ sage: c.save(f.name, figsize=5, xmin=-1, xmax=3, ymin=-1, ymax=3)
3299
+
3300
+ By default, the figure grows to include all of the graphics and text,
3301
+ so the final image may not be exactly the figure size you specified.
3302
+ If you want a figure to be exactly a certain size, specify the keyword
3303
+ ``fig_tight=False``::
3304
+
3305
+ sage: c.save(f.name, figsize=[8,4], fig_tight=False,
3306
+ ....: xmin=-1, xmax=3, ymin=-1, ymax=3)
3307
+
3308
+ You can also pass extra options to the plot command instead of this
3309
+ method, e.g. ::
3310
+
3311
+ sage: plot(x^2 - 5, (x, 0, 5), ymin=0).save(tmp_filename(ext='.png')) # needs sage.symbolic
3312
+
3313
+ will save the same plot as the one shown by this command::
3314
+
3315
+ sage: plot(x^2 - 5, (x, 0, 5), ymin=0) # needs sage.symbolic
3316
+ Graphics object consisting of 1 graphics primitive
3317
+
3318
+ (This test verifies that :issue:`8632` is fixed.)
3319
+
3320
+ TESTS:
3321
+
3322
+ Legend labels should save correctly::
3323
+
3324
+ sage: # needs sage.symbolic
3325
+ sage: P = plot(x,(x,0,1),legend_label='$xyz$')
3326
+ sage: P.set_legend_options(back_color=(1,0,0))
3327
+ sage: P.set_legend_options(loc=7)
3328
+ sage: import tempfile
3329
+ sage: with tempfile.NamedTemporaryFile(suffix='.png') as f:
3330
+ ....: P.save(f.name)
3331
+
3332
+ This plot should save with the frame shown, showing :issue:`7524`
3333
+ is fixed (same issue as :issue:`7981` and :issue:`8632`)::
3334
+
3335
+ sage: var('x,y') # needs sage.symbolic
3336
+ (x, y)
3337
+ sage: a = plot_vector_field((x,-y),(x,-1,1),(y,-1,1)) # needs sage.symbolic
3338
+ sage: import tempfile
3339
+ sage: with tempfile.NamedTemporaryFile(suffix='.png') as f: # needs sage.symbolic
3340
+ ....: a.save(f.name)
3341
+
3342
+ The following plot should show the axes; fixes :issue:`14782` ::
3343
+
3344
+ sage: plot(x^2, (x, 1, 2), ticks=[[], []]) # needs sage.symbolic
3345
+ Graphics object consisting of 1 graphics primitive
3346
+ """
3347
+ options = {}
3348
+ options.update(self.SHOW_OPTIONS)
3349
+ options.update(self._extra_kwds)
3350
+ options.update(kwds)
3351
+ dpi = options.pop('dpi')
3352
+ transparent = options.pop('transparent')
3353
+ fig_tight = options.pop('fig_tight')
3354
+
3355
+ ext = os.path.splitext(filename)[1].lower()
3356
+
3357
+ if ext in ['', '.sobj']:
3358
+ SageObject.save(self, filename)
3359
+ elif ext not in ALLOWED_EXTENSIONS:
3360
+ raise ValueError("allowed file extensions for images are '" +
3361
+ "', '".join(ALLOWED_EXTENSIONS) + "'!")
3362
+ else:
3363
+ from matplotlib import rcParams
3364
+ rc_backup = (rcParams['ps.useafm'], rcParams['pdf.use14corefonts'],
3365
+ rcParams['text.usetex']) # save the rcParams
3366
+ figure = self.matplotlib(**options)
3367
+ # You can output in PNG, PS, EPS, PDF, PGF, or SVG format, depending
3368
+ # on the file extension.
3369
+ # PGF is handled by a different backend
3370
+ if ext == '.pgf':
3371
+ from sage.features.latex import xelatex, pdflatex, lualatex
3372
+ latex_implementations = []
3373
+ if xelatex().is_present():
3374
+ latex_implementations.append('xelatex')
3375
+ if pdflatex().is_present():
3376
+ latex_implementations.append('pdflatex')
3377
+ if lualatex().is_present():
3378
+ latex_implementations.append('lualatex')
3379
+ if not latex_implementations:
3380
+ raise ValueError("Matplotlib requires either xelatex, "
3381
+ "lualatex, or pdflatex.")
3382
+ if latex_implementations[0] == "pdflatex":
3383
+ # use pdflatex and set font encoding as per
3384
+ # matplotlib documentation:
3385
+ # https://matplotlib.org/users/pgf.html#pgf-tutorial
3386
+ pgf_options = {"pgf.texsystem": "pdflatex",
3387
+ "pgf.preamble": [
3388
+ r"\usepackage[utf8x]{inputenc}",
3389
+ r"\usepackage[T1]{fontenc}"]}
3390
+ else:
3391
+ pgf_options = {
3392
+ "pgf.texsystem": latex_implementations[0],
3393
+ }
3394
+ from matplotlib import rcParams
3395
+ rcParams.update(pgf_options)
3396
+ from matplotlib.backends.backend_pgf import FigureCanvasPgf
3397
+ figure.set_canvas(FigureCanvasPgf(figure))
3398
+
3399
+ # matplotlib looks at the file extension to see what the renderer should be.
3400
+ # The default is FigureCanvasAgg for PNG's because this is by far the most
3401
+ # common type of files rendered, like in the notebook, for example.
3402
+ # if the file extension is not '.png', then matplotlib will handle it.
3403
+ else:
3404
+ from matplotlib.backends.backend_agg import FigureCanvasAgg
3405
+ figure.set_canvas(FigureCanvasAgg(figure))
3406
+ # this messes up the aspect ratio!
3407
+ # figure.canvas.mpl_connect('draw_event', pad_for_tick_labels)
3408
+
3409
+ # tight_layout adjusts the *subplot* parameters so ticks aren't cut off, etc.
3410
+ figure.tight_layout()
3411
+
3412
+ opts = {'dpi': dpi, 'transparent': transparent}
3413
+ if fig_tight is True:
3414
+ opts['bbox_inches'] = 'tight'
3415
+ if self._bbox_extra_artists:
3416
+ opts['bbox_extra_artists'] = self._bbox_extra_artists
3417
+
3418
+ figure.savefig(filename, **opts)
3419
+
3420
+ # Restore the rcParams to the original, possibly user-set values
3421
+ (rcParams['ps.useafm'], rcParams['pdf.use14corefonts'],
3422
+ rcParams['text.usetex']) = rc_backup
3423
+
3424
+ def _latex_(self, **kwds):
3425
+ """
3426
+ Return a string plotting ``self`` with PGF.
3427
+
3428
+ INPUT:
3429
+
3430
+ - ``**kwds`` -- all keyword arguments will be passed to the plotter
3431
+
3432
+ OUTPUT: string of PGF commands to plot ``self``
3433
+
3434
+ EXAMPLES::
3435
+
3436
+ sage: L = line([(0,0), (1,1)], axes=False)
3437
+ sage: L._latex_() # not tested
3438
+ '%% Creator: Matplotlib, PGF backend...
3439
+ """
3440
+ tmpfilename = tmp_filename(ext='.pgf')
3441
+ self.save(filename=tmpfilename, **kwds)
3442
+ with open(tmpfilename) as tmpfile:
3443
+ latex_list = tmpfile.readlines()
3444
+ from sage.misc.latex import latex
3445
+ latex.add_package_to_preamble_if_available('pgf')
3446
+ return ''.join(latex_list)
3447
+
3448
+ def description(self):
3449
+ r"""
3450
+ Print a textual description to stdout.
3451
+
3452
+ This method is mostly used for doctests.
3453
+
3454
+ EXAMPLES::
3455
+
3456
+ sage: print(polytopes.hypercube(2).plot().description()) # needs sage.geometry.polyhedron
3457
+ Polygon defined by 4 points: [(-1.0, -1.0), (1.0, -1.0), (1.0, 1.0), (-1.0, 1.0)]
3458
+ Line defined by 2 points: [(-1.0, 1.0), (-1.0, -1.0)]
3459
+ Line defined by 2 points: [(1.0, -1.0), (-1.0, -1.0)]
3460
+ Line defined by 2 points: [(1.0, -1.0), (1.0, 1.0)]
3461
+ Line defined by 2 points: [(1.0, 1.0), (-1.0, 1.0)]
3462
+ Point set defined by 4 point(s): [(1.0, -1.0), (1.0, 1.0), (-1.0, 1.0), (-1.0, -1.0)]
3463
+ """
3464
+ data = []
3465
+ for g in self:
3466
+ g_zorder = g.options().get('zorder', 0)
3467
+ if hasattr(g, 'xdata'):
3468
+ g_str = f'{g}:\t{list(zip(g.xdata, g.ydata))}'
3469
+ else:
3470
+ g_str = repr(g)
3471
+ data.append([g_zorder, g_str, g])
3472
+ data.sort()
3473
+ return '\n'.join(g[1] for g in data)
3474
+
3475
+ def inset(self, graphics, pos=None, fontsize=None):
3476
+ r"""
3477
+ Add a graphics object as an inset.
3478
+
3479
+ INPUT:
3480
+
3481
+ - ``graphics`` -- the graphics object (instance of :class:`Graphics`)
3482
+ to be added as an inset to the current graphics
3483
+
3484
+ - ``pos`` -- (default: ``None``) 4-tuple
3485
+ ``(left, bottom, width, height)``
3486
+ specifying the location and size of the inset on the final figure,
3487
+ all quantities being in fractions of the figure width and height; if
3488
+ ``None``, the value ``(0.7, 0.7, 0.2, 0.2)`` is used
3489
+
3490
+ - ``fontsize`` -- (default: ``None``) integer, font size (in points)
3491
+ for the inset; if ``None``, the value of 6 points is used, unless
3492
+ ``fontsize`` has been explicitly set in the construction of
3493
+ ``graphics`` (in this case, it is not overwritten here)
3494
+
3495
+ OUTPUT: instance of :class:`~sage.plot.multigraphics.MultiGraphics`
3496
+
3497
+ EXAMPLES::
3498
+
3499
+ sage: # needs sage.symbolic
3500
+ sage: f(x) = x^2*sin(1/x)
3501
+ sage: g1 = plot(f(x), (x, -2, 2), axes_labels=['$x$', '$y$'])
3502
+ sage: g2 = plot(f(x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'],
3503
+ ....: frame=True)
3504
+ sage: g1.inset(g2)
3505
+ Multigraphics with 2 elements
3506
+
3507
+ .. PLOT::
3508
+
3509
+ f = (x**2*sin(1/x)).function(x)
3510
+ g1 = plot(f(x), (x, -2, 2), axes_labels=['$x$', '$y$'])
3511
+ g2 = plot(f(x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'], \
3512
+ frame=True)
3513
+ sphinx_plot(g1.inset(g2))
3514
+
3515
+ Using non-default values for the position/size and the font size::
3516
+
3517
+ sage: g1.inset(g2, pos=(0.15, 0.7, 0.25, 0.25), fontsize=8) # needs sage.symbolic
3518
+ Multigraphics with 2 elements
3519
+
3520
+ .. PLOT::
3521
+
3522
+ f = (x**2*sin(1/x)).function(x)
3523
+ g1 = plot(f(x), (x, -2, 2), axes_labels=['$x$', '$y$'])
3524
+ g2 = plot(f(x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'], \
3525
+ frame=True)
3526
+ sphinx_plot(g1.inset(g2, pos=(0.15, 0.7, 0.25, 0.25), fontsize=8))
3527
+
3528
+ We can add another inset by invoking ``inset`` on the last output::
3529
+
3530
+ sage: g1g2 = _ # needs sage.symbolic
3531
+ sage: g3 = plot(f(x), (x, -0.05, 0.05), axes_labels=['$x$', '$y$'], # needs sage.symbolic
3532
+ ....: frame=True)
3533
+ sage: g1g2.inset(g3, pos=(0.65, 0.12, 0.25, 0.25)) # needs sage.symbolic
3534
+ Multigraphics with 3 elements
3535
+
3536
+ .. PLOT::
3537
+
3538
+ f = (x**2*sin(1/x)).function(x)
3539
+ g1 = plot(f(x), (x, -2, 2), axes_labels=['$x$', '$y$'])
3540
+ g2 = plot(f(x), (x, -0.3, 0.3), axes_labels=['$x$', '$y$'], \
3541
+ frame=True)
3542
+ g1g2 = g1.inset(g2, pos=(0.15, 0.7, 0.25, 0.25), fontsize=8)
3543
+ g3 = plot(f(x), (x, -0.05, 0.05), axes_labels=['$x$', '$y$'], \
3544
+ frame=True)
3545
+ sphinx_plot(g1g2.inset(g3, pos=(0.65, 0.12, 0.25, 0.25)))
3546
+ """
3547
+ from .multigraphics import MultiGraphics
3548
+ if pos is None:
3549
+ pos = (0.7, 0.7, 0.2, 0.2)
3550
+ pos0 = (0.05, 0.05, 0.9, 0.9)
3551
+ if fontsize is not None:
3552
+ graphics._extra_kwds['fontsize'] = fontsize
3553
+ elif 'fontsize' not in graphics._extra_kwds:
3554
+ graphics._extra_kwds['fontsize'] = 6
3555
+ return MultiGraphics([(self, pos0), (graphics, pos)])
3556
+
3557
+
3558
+ # Deprecation notice for GraphicsArray import
3559
+ def GraphicsArray(*args, **kwargs):
3560
+ r"""
3561
+ This is deprecated (see :issue:`28675`).
3562
+ Use :class:`sage.plot.multigraphics.GraphicsArray` instead.
3563
+
3564
+ TESTS::
3565
+
3566
+ sage: from sage.plot.graphics import GraphicsArray
3567
+ sage: c = circle((0,0), 1)
3568
+ sage: G = GraphicsArray([c, c])
3569
+ doctest:...: DeprecationWarning: GraphicsArray must be imported from
3570
+ sage.plot.multigraphics and no longer from sage.plot.graphics.
3571
+ See https://github.com/sagemath/sage/issues/28675 for details.
3572
+ sage: G
3573
+ Graphics Array of size 1 x 2
3574
+ """
3575
+ from .multigraphics import GraphicsArray as NewGraphicsArray
3576
+ from sage.misc.superseded import deprecation
3577
+ deprecation(28675, "GraphicsArray must be imported from "
3578
+ "sage.plot.multigraphics and no longer from "
3579
+ "sage.plot.graphics.")
3580
+ return NewGraphicsArray(*args, **kwargs)