passagemath-plot 10.6.31rc3__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of passagemath-plot might be problematic. Click here for more details.
- passagemath_plot-10.6.31rc3.dist-info/METADATA +172 -0
- passagemath_plot-10.6.31rc3.dist-info/RECORD +81 -0
- passagemath_plot-10.6.31rc3.dist-info/WHEEL +6 -0
- passagemath_plot-10.6.31rc3.dist-info/top_level.txt +2 -0
- passagemath_plot.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- passagemath_plot.libs/libgsl-e3525837.so.28.0.0 +0 -0
- passagemath_plot.libs/libopenblasp-r0-4c5b64b1.3.29.so +0 -0
- sage/all__sagemath_plot.py +15 -0
- sage/ext_data/threejs/animation.css +195 -0
- sage/ext_data/threejs/animation.html +85 -0
- sage/ext_data/threejs/animation.js +273 -0
- sage/ext_data/threejs/fat_lines.js +48 -0
- sage/ext_data/threejs/threejs-version.txt +1 -0
- sage/ext_data/threejs/threejs_template.html +597 -0
- sage/interfaces/all__sagemath_plot.py +1 -0
- sage/interfaces/gnuplot.py +196 -0
- sage/interfaces/jmoldata.py +208 -0
- sage/interfaces/povray.py +56 -0
- sage/plot/all.py +42 -0
- sage/plot/animate.py +1796 -0
- sage/plot/arc.py +504 -0
- sage/plot/arrow.py +671 -0
- sage/plot/bar_chart.py +205 -0
- sage/plot/bezier_path.py +400 -0
- sage/plot/circle.py +435 -0
- sage/plot/colors.py +1606 -0
- sage/plot/complex_plot.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/complex_plot.pyx +1446 -0
- sage/plot/contour_plot.py +1792 -0
- sage/plot/density_plot.py +318 -0
- sage/plot/disk.py +373 -0
- sage/plot/ellipse.py +375 -0
- sage/plot/graphics.py +3580 -0
- sage/plot/histogram.py +354 -0
- sage/plot/hyperbolic_arc.py +404 -0
- sage/plot/hyperbolic_polygon.py +416 -0
- sage/plot/hyperbolic_regular_polygon.py +296 -0
- sage/plot/line.py +626 -0
- sage/plot/matrix_plot.py +629 -0
- sage/plot/misc.py +509 -0
- sage/plot/multigraphics.py +1294 -0
- sage/plot/plot.py +4183 -0
- sage/plot/plot3d/all.py +23 -0
- sage/plot/plot3d/base.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/base.pxd +12 -0
- sage/plot/plot3d/base.pyx +3378 -0
- sage/plot/plot3d/implicit_plot3d.py +659 -0
- sage/plot/plot3d/implicit_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/index_face_set.pxd +32 -0
- sage/plot/plot3d/index_face_set.pyx +1873 -0
- sage/plot/plot3d/introduction.py +131 -0
- sage/plot/plot3d/list_plot3d.py +649 -0
- sage/plot/plot3d/parametric_plot3d.py +1130 -0
- sage/plot/plot3d/parametric_surface.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/parametric_surface.pxd +12 -0
- sage/plot/plot3d/parametric_surface.pyx +893 -0
- sage/plot/plot3d/platonic.py +601 -0
- sage/plot/plot3d/plot3d.py +1442 -0
- sage/plot/plot3d/plot_field3d.py +162 -0
- sage/plot/plot3d/point_c.pxi +148 -0
- sage/plot/plot3d/revolution_plot3d.py +309 -0
- sage/plot/plot3d/shapes.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/shapes.pxd +22 -0
- sage/plot/plot3d/shapes.pyx +1382 -0
- sage/plot/plot3d/shapes2.py +1512 -0
- sage/plot/plot3d/tachyon.py +1779 -0
- sage/plot/plot3d/texture.py +453 -0
- sage/plot/plot3d/transform.cpython-314-aarch64-linux-gnu.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- sage/plot/text.py +432 -0
|
@@ -0,0 +1,1294 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-plot
|
|
2
|
+
# sage.doctest: needs sage.symbolic
|
|
3
|
+
r"""
|
|
4
|
+
Graphics arrays and insets
|
|
5
|
+
|
|
6
|
+
This module defines the classes :class:`MultiGraphics` and
|
|
7
|
+
:class:`GraphicsArray`. The class :class:`MultiGraphics` is the base class
|
|
8
|
+
for 2-dimensional graphical objects that are composed of various
|
|
9
|
+
:class:`~sage.plot.graphics.Graphics` objects, arranged in a given canvas.
|
|
10
|
+
The subclass :class:`GraphicsArray` is for
|
|
11
|
+
:class:`~sage.plot.graphics.Graphics` objects arranged in a regular array.
|
|
12
|
+
|
|
13
|
+
AUTHORS:
|
|
14
|
+
|
|
15
|
+
- Eric Gourgoulhon (2019-05-24): initial version, refactoring the class
|
|
16
|
+
``GraphicsArray`` that was defined in the module :mod:`~sage.plot.graphics`.
|
|
17
|
+
"""
|
|
18
|
+
import os
|
|
19
|
+
from sage.misc.fast_methods import WithEqualityById
|
|
20
|
+
from sage.structure.sage_object import SageObject
|
|
21
|
+
from sage.misc.temporary_file import tmp_filename
|
|
22
|
+
from .graphics import Graphics, ALLOWED_EXTENSIONS, _parse_figsize
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MultiGraphics(WithEqualityById, SageObject):
|
|
26
|
+
r"""
|
|
27
|
+
Base class for objects composed of :class:`~sage.plot.graphics.Graphics`
|
|
28
|
+
objects.
|
|
29
|
+
|
|
30
|
+
Both the display and the output to a file of ``MultiGraphics`` objects
|
|
31
|
+
are governed by the method :meth:`save`, which is called by the rich output
|
|
32
|
+
display manager, via
|
|
33
|
+
:meth:`~sage.repl.rich_output.display_manager.DisplayManager.graphics_from_save`.
|
|
34
|
+
|
|
35
|
+
The user interface is through the functions
|
|
36
|
+
:func:`~sage.plot.plot.multi_graphics` (generic multi-graphics) and
|
|
37
|
+
:func:`~sage.plot.plot.graphics_array` (subclass :class:`GraphicsArray`).
|
|
38
|
+
|
|
39
|
+
INPUT:
|
|
40
|
+
|
|
41
|
+
- ``graphics_list`` -- list of graphics along with their positions on the
|
|
42
|
+
common canvas; each element of ``graphics_list`` is either
|
|
43
|
+
|
|
44
|
+
- a pair ``(graphics, position)``, where ``graphics`` is a
|
|
45
|
+
:class:`~sage.plot.graphics.Graphics` object and ``position`` is the
|
|
46
|
+
4-tuple ``(left, bottom, width, height)`` specifying the location and
|
|
47
|
+
size of the graphics on the canvas, all quantities being in fractions
|
|
48
|
+
of the canvas width and height
|
|
49
|
+
|
|
50
|
+
- or a single :class:`~sage.plot.graphics.Graphics` object; its position
|
|
51
|
+
is then assumed to occupy the whole canvas, except for some padding;
|
|
52
|
+
this corresponds to the default position
|
|
53
|
+
``(left, bottom, width, height) = (0.125, 0.11, 0.775, 0.77)``
|
|
54
|
+
|
|
55
|
+
EXAMPLES:
|
|
56
|
+
|
|
57
|
+
A multi-graphics made from two graphics objects::
|
|
58
|
+
|
|
59
|
+
sage: g1 = plot(sin(x^3), (x, -pi, pi))
|
|
60
|
+
sage: g2 = circle((0,0), 1, color='red')
|
|
61
|
+
sage: G = multi_graphics([g1, (g2, (0.2, 0.55, 0.3, 0.3))])
|
|
62
|
+
sage: G
|
|
63
|
+
Multigraphics with 2 elements
|
|
64
|
+
|
|
65
|
+
.. PLOT::
|
|
66
|
+
|
|
67
|
+
g1 = plot(sin(x**3), (x, -pi, pi))
|
|
68
|
+
g2 = circle((0,0), 1, color='red')
|
|
69
|
+
G = multi_graphics([g1, (g2, (0.2, 0.55, 0.3, 0.3))])
|
|
70
|
+
sphinx_plot(G)
|
|
71
|
+
|
|
72
|
+
Since no position was given for ``g1``, it occupies the whole canvas.
|
|
73
|
+
Moreover, we note that ``g2`` has been drawn over ``g1`` with a white
|
|
74
|
+
background. To have a transparent background instead, one has to construct
|
|
75
|
+
``g2`` with the keyword ``transparent`` set to ``True``::
|
|
76
|
+
|
|
77
|
+
sage: g2 = circle((0,0), 1, color='red', transparent=True)
|
|
78
|
+
sage: G = multi_graphics([g1, (g2, (0.2, 0.55, 0.3, 0.3))])
|
|
79
|
+
sage: G
|
|
80
|
+
Multigraphics with 2 elements
|
|
81
|
+
|
|
82
|
+
.. PLOT::
|
|
83
|
+
|
|
84
|
+
g1 = plot(sin(x**3), (x, -pi, pi))
|
|
85
|
+
g2 = circle((0,0), 1, color='red', transparent=True)
|
|
86
|
+
G = multi_graphics([g1, (g2, (0.2, 0.55, 0.3, 0.3))])
|
|
87
|
+
sphinx_plot(G)
|
|
88
|
+
|
|
89
|
+
We can add a new graphics object to G via the method :meth:`append`::
|
|
90
|
+
|
|
91
|
+
sage: g3 = complex_plot(zeta, (-20, 10), (-20, 20),
|
|
92
|
+
....: axes_labels=['$x$', '$y$'], frame=True)
|
|
93
|
+
sage: G.append(g3, pos=(0.63, 0.12, 0.3, 0.3))
|
|
94
|
+
sage: G
|
|
95
|
+
Multigraphics with 3 elements
|
|
96
|
+
|
|
97
|
+
.. PLOT::
|
|
98
|
+
|
|
99
|
+
g1 = plot(sin(x**3), (x, -pi, pi))
|
|
100
|
+
g2 = circle((0,0), 1, color='red', transparent=True)
|
|
101
|
+
G = multi_graphics([g1, (g2, (0.2, 0.55, 0.3, 0.3))])
|
|
102
|
+
g3 = complex_plot(zeta, (-20, 10), (-20, 20), \
|
|
103
|
+
axes_labels=['$x$', '$y$'], frame=True)
|
|
104
|
+
G.append(g3, pos=(0.63, 0.12, 0.3, 0.3))
|
|
105
|
+
sphinx_plot(G)
|
|
106
|
+
|
|
107
|
+
We can access the individual elements composing ``G`` with the
|
|
108
|
+
square-bracket operator::
|
|
109
|
+
|
|
110
|
+
sage: print(G[0])
|
|
111
|
+
Graphics object consisting of 1 graphics primitive
|
|
112
|
+
sage: G[0] is g1
|
|
113
|
+
True
|
|
114
|
+
sage: G[1] is g2
|
|
115
|
+
True
|
|
116
|
+
sage: G[2] is g3
|
|
117
|
+
True
|
|
118
|
+
|
|
119
|
+
``G[:]`` returns the full list of graphics objects composing ``G``::
|
|
120
|
+
|
|
121
|
+
sage: G[:]
|
|
122
|
+
[Graphics object consisting of 1 graphics primitive,
|
|
123
|
+
Graphics object consisting of 1 graphics primitive,
|
|
124
|
+
Graphics object consisting of 1 graphics primitive]
|
|
125
|
+
sage: len(G)
|
|
126
|
+
3
|
|
127
|
+
"""
|
|
128
|
+
def __init__(self, graphics_list):
|
|
129
|
+
r"""
|
|
130
|
+
Initialize the attributes common to all MultiGraphics objects.
|
|
131
|
+
|
|
132
|
+
TESTS::
|
|
133
|
+
|
|
134
|
+
sage: from sage.plot.multigraphics import MultiGraphics
|
|
135
|
+
sage: G = MultiGraphics([])
|
|
136
|
+
sage: print(G)
|
|
137
|
+
Multigraphics with 0 element
|
|
138
|
+
sage: c = circle((0,0), 1)
|
|
139
|
+
sage: G = MultiGraphics([c, (c, (0.7, 0.6, 0.2, 0.2))])
|
|
140
|
+
sage: print(G)
|
|
141
|
+
Multigraphics with 2 elements
|
|
142
|
+
"""
|
|
143
|
+
self._glist = []
|
|
144
|
+
self._positions = []
|
|
145
|
+
|
|
146
|
+
for ins in graphics_list:
|
|
147
|
+
if isinstance(ins, Graphics):
|
|
148
|
+
self.append(ins) # default position
|
|
149
|
+
else:
|
|
150
|
+
if not isinstance(ins, (list, tuple)) or len(ins) != 2:
|
|
151
|
+
raise TypeError("a pair (Graphics, position) is "
|
|
152
|
+
f"expected, not {ins}")
|
|
153
|
+
self.append(ins[0], pos=ins[1])
|
|
154
|
+
|
|
155
|
+
def _repr_(self):
|
|
156
|
+
r"""
|
|
157
|
+
Representation of ``self``.
|
|
158
|
+
|
|
159
|
+
EXAMPLES::
|
|
160
|
+
|
|
161
|
+
sage: c = circle((0,0), 1)
|
|
162
|
+
sage: G = graphics_array([c, c, c])
|
|
163
|
+
sage: G._repr_()
|
|
164
|
+
'Graphics Array of size 1 x 3'
|
|
165
|
+
sage: G
|
|
166
|
+
Graphics Array of size 1 x 3
|
|
167
|
+
"""
|
|
168
|
+
return str(self)
|
|
169
|
+
|
|
170
|
+
def _rich_repr_(self, display_manager, **kwds):
|
|
171
|
+
r"""
|
|
172
|
+
Rich Output Magic Method.
|
|
173
|
+
|
|
174
|
+
See :mod:`sage.repl.rich_output` for details.
|
|
175
|
+
|
|
176
|
+
.. TODO::
|
|
177
|
+
|
|
178
|
+
This method is identical to Graphics._rich_repr_ so it could be
|
|
179
|
+
inherited from a common base class
|
|
180
|
+
|
|
181
|
+
EXAMPLES::
|
|
182
|
+
|
|
183
|
+
sage: from sage.repl.rich_output import get_display_manager
|
|
184
|
+
sage: dm = get_display_manager()
|
|
185
|
+
sage: G = graphics_array([Graphics(), Graphics()], 1, 2)
|
|
186
|
+
sage: G._rich_repr_(dm)
|
|
187
|
+
OutputImagePng container
|
|
188
|
+
"""
|
|
189
|
+
types = display_manager.types
|
|
190
|
+
prefer_raster = (
|
|
191
|
+
('.png', types.OutputImagePng),
|
|
192
|
+
('.jpg', types.OutputImageJpg),
|
|
193
|
+
('.gif', types.OutputImageGif),
|
|
194
|
+
)
|
|
195
|
+
prefer_vector = (
|
|
196
|
+
('.svg', types.OutputImageSvg),
|
|
197
|
+
('.pdf', types.OutputImagePdf),
|
|
198
|
+
)
|
|
199
|
+
graphics = display_manager.preferences.graphics
|
|
200
|
+
if graphics == 'disable':
|
|
201
|
+
return
|
|
202
|
+
elif graphics == 'raster' or graphics is None:
|
|
203
|
+
preferred = prefer_raster + prefer_vector
|
|
204
|
+
elif graphics == 'vector':
|
|
205
|
+
preferred = prefer_vector + prefer_raster
|
|
206
|
+
else:
|
|
207
|
+
raise ValueError('unknown graphics output preference')
|
|
208
|
+
for file_ext, output_container in preferred:
|
|
209
|
+
if output_container in display_manager.supported_output():
|
|
210
|
+
return display_manager.graphics_from_save(
|
|
211
|
+
self.save, kwds, file_ext, output_container)
|
|
212
|
+
|
|
213
|
+
def __getitem__(self, i):
|
|
214
|
+
r"""
|
|
215
|
+
Return the ``i``-th element of the list of graphics composing ``self``.
|
|
216
|
+
|
|
217
|
+
EXAMPLES:
|
|
218
|
+
|
|
219
|
+
We can access and view individual plots::
|
|
220
|
+
|
|
221
|
+
sage: L = [[plot(x^2)], [plot(x^3)]]
|
|
222
|
+
sage: G = graphics_array(L)
|
|
223
|
+
sage: G[1]
|
|
224
|
+
Graphics object consisting of 1 graphics primitive
|
|
225
|
+
|
|
226
|
+
Another example::
|
|
227
|
+
|
|
228
|
+
sage: L = [plot(sin(k*x), (x,-pi,pi)) + circle((k,k), 1,
|
|
229
|
+
....: color='red') for k in range(10)]
|
|
230
|
+
sage: G = graphics_array(L, 5, 2)
|
|
231
|
+
sage: G[3]
|
|
232
|
+
Graphics object consisting of 2 graphics primitives
|
|
233
|
+
"""
|
|
234
|
+
return self._glist[i]
|
|
235
|
+
|
|
236
|
+
def __setitem__(self, i, g):
|
|
237
|
+
r"""
|
|
238
|
+
Set the ``i``-th element of the list of graphics composing ``self``.
|
|
239
|
+
|
|
240
|
+
EXAMPLES::
|
|
241
|
+
|
|
242
|
+
sage: L = [[plot(x^2)], [plot(x^3)]]
|
|
243
|
+
sage: G = graphics_array(L)
|
|
244
|
+
sage: G[1] # the plot of x^3
|
|
245
|
+
Graphics object consisting of 1 graphics primitive
|
|
246
|
+
|
|
247
|
+
Now we change it::
|
|
248
|
+
|
|
249
|
+
sage: G[1] = circle((1,1), 2) + points([(1,2), (3,2), (5,5)],
|
|
250
|
+
....: color='purple')
|
|
251
|
+
sage: G[1] # a circle and some purple points
|
|
252
|
+
Graphics object consisting of 2 graphics primitives
|
|
253
|
+
"""
|
|
254
|
+
self._glist[i] = g
|
|
255
|
+
|
|
256
|
+
def __len__(self):
|
|
257
|
+
r"""
|
|
258
|
+
Total number of Graphics objects composing ``self``.
|
|
259
|
+
|
|
260
|
+
EXAMPLES::
|
|
261
|
+
|
|
262
|
+
sage: L = [circle((0,0), n) for n in range(6)]
|
|
263
|
+
sage: G = graphics_array(L, 2, 3)
|
|
264
|
+
sage: len(G)
|
|
265
|
+
6
|
|
266
|
+
"""
|
|
267
|
+
return len(self._glist)
|
|
268
|
+
|
|
269
|
+
def matplotlib(self, figure=None, figsize=None, **kwds):
|
|
270
|
+
r"""
|
|
271
|
+
Construct or modify a Matplotlib figure by drawing ``self`` on it.
|
|
272
|
+
|
|
273
|
+
INPUT:
|
|
274
|
+
|
|
275
|
+
- ``figure`` -- (default: ``None``) Matplotlib figure (class
|
|
276
|
+
``matplotlib.figure.Figure``) on which ``self`` is to be displayed;
|
|
277
|
+
if ``None``, the figure will be created from the parameter
|
|
278
|
+
``figsize``
|
|
279
|
+
|
|
280
|
+
- ``figsize`` -- (default: ``None``) width or [width, height] in inches
|
|
281
|
+
of the Matplotlib figure in case ``figure`` is ``None``; if
|
|
282
|
+
``figsize`` is ``None``, Matplotlib's default (6.4 x 4.8 inches) is
|
|
283
|
+
used
|
|
284
|
+
|
|
285
|
+
- ``kwds`` -- options passed to the
|
|
286
|
+
:meth:`~sage.plot.graphics.Graphics.matplotlib` method of
|
|
287
|
+
each graphics object constituting ``self``
|
|
288
|
+
|
|
289
|
+
OUTPUT:
|
|
290
|
+
|
|
291
|
+
- a ``matplotlib.figure.Figure`` object; if the argument ``figure`` is
|
|
292
|
+
provided, this is the same object as ``figure``.
|
|
293
|
+
|
|
294
|
+
EXAMPLES:
|
|
295
|
+
|
|
296
|
+
Let us consider a :class:`GraphicsArray` object with 3 elements::
|
|
297
|
+
|
|
298
|
+
sage: G = graphics_array([plot(sin(x^k), (x, 0, 3))
|
|
299
|
+
....: for k in range(1, 4)])
|
|
300
|
+
|
|
301
|
+
If ``matplotlib()`` is invoked without any argument, a Matplotlib
|
|
302
|
+
figure is created and contains the 3 graphics element of the array
|
|
303
|
+
as 3 Matplotlib ``Axes``::
|
|
304
|
+
|
|
305
|
+
sage: fig = G.matplotlib()
|
|
306
|
+
sage: fig
|
|
307
|
+
<Figure size 640x480 with 3 Axes>
|
|
308
|
+
sage: type(fig)
|
|
309
|
+
<class 'matplotlib.figure.Figure'>
|
|
310
|
+
|
|
311
|
+
Specifying the figure size (in inches)::
|
|
312
|
+
|
|
313
|
+
sage: G.matplotlib(figsize=(8., 5.))
|
|
314
|
+
<Figure size 800x500 with 3 Axes>
|
|
315
|
+
|
|
316
|
+
If a single number is provided for ``figsize``, it is considered to be
|
|
317
|
+
the width; the height is then computed according to Matplotlib's
|
|
318
|
+
default aspect ratio (4/3)::
|
|
319
|
+
|
|
320
|
+
sage: G.matplotlib(figsize=8.)
|
|
321
|
+
<Figure size 800x600 with 3 Axes>
|
|
322
|
+
|
|
323
|
+
An example of use with a preexisting created figure, created by
|
|
324
|
+
``pyplot``::
|
|
325
|
+
|
|
326
|
+
sage: import matplotlib.pyplot as plt
|
|
327
|
+
sage: fig1 = plt.figure(1)
|
|
328
|
+
sage: fig1
|
|
329
|
+
<Figure size 640x480 with 0 Axes>
|
|
330
|
+
sage: fig_out = G.matplotlib(figure=fig1)
|
|
331
|
+
sage: fig_out
|
|
332
|
+
<Figure size 640x480 with 3 Axes>
|
|
333
|
+
|
|
334
|
+
Note that the output figure is the same object as the input one::
|
|
335
|
+
|
|
336
|
+
sage: fig_out is fig1
|
|
337
|
+
True
|
|
338
|
+
|
|
339
|
+
It has however been modified by ``G.matplotlib(figure=fig1)``, which
|
|
340
|
+
has added 3 new ``Axes`` to it.
|
|
341
|
+
|
|
342
|
+
Another example, with a figure created from scratch, via Matplolib's
|
|
343
|
+
``Figure``::
|
|
344
|
+
|
|
345
|
+
sage: from matplotlib.figure import Figure
|
|
346
|
+
sage: fig2 = Figure()
|
|
347
|
+
sage: fig2
|
|
348
|
+
<Figure size 640x480 with 0 Axes>
|
|
349
|
+
sage: G.matplotlib(figure=fig2)
|
|
350
|
+
<Figure size 640x480 with 3 Axes>
|
|
351
|
+
sage: fig2
|
|
352
|
+
<Figure size 640x480 with 3 Axes>
|
|
353
|
+
"""
|
|
354
|
+
from matplotlib.figure import Figure
|
|
355
|
+
glist = self._glist
|
|
356
|
+
if len(glist) == 0: # for an empty MultiGraphics, we create
|
|
357
|
+
glist = [Graphics()] # a 1-element list with an empty graphics
|
|
358
|
+
# If no Matplotlib figure is provided, it is created here:
|
|
359
|
+
if figure is None:
|
|
360
|
+
if figsize is not None:
|
|
361
|
+
figsize = _parse_figsize(figsize)
|
|
362
|
+
figure = Figure(figsize=figsize)
|
|
363
|
+
global do_verify
|
|
364
|
+
do_verify = True
|
|
365
|
+
for i, g in enumerate(glist):
|
|
366
|
+
# Options for g.matplotlib():
|
|
367
|
+
options = {}
|
|
368
|
+
options.update(Graphics.SHOW_OPTIONS) # default options for show()
|
|
369
|
+
options['legend_options'] = Graphics.LEGEND_OPTIONS # default leg.
|
|
370
|
+
options.update(g._extra_kwds) # options set in g
|
|
371
|
+
options.update(kwds)
|
|
372
|
+
# We get rid of options that are not relevant for g.matplotlib():
|
|
373
|
+
options.pop('dpi', None)
|
|
374
|
+
options.pop('fig_tight', None)
|
|
375
|
+
transparent = options.pop('transparent', None)
|
|
376
|
+
# Creating the Matplotlib Axes object "subplot" on the figure:
|
|
377
|
+
subplot = self._add_subplot(figure, i)
|
|
378
|
+
# and drawing g on it:
|
|
379
|
+
g.matplotlib(figure=figure, sub=subplot, verify=do_verify,
|
|
380
|
+
**options)
|
|
381
|
+
if transparent:
|
|
382
|
+
subplot.set_facecolor('none')
|
|
383
|
+
return figure
|
|
384
|
+
|
|
385
|
+
def save(self, filename, figsize=None, **kwds):
|
|
386
|
+
r"""
|
|
387
|
+
Save ``self`` to a file, in various formats.
|
|
388
|
+
|
|
389
|
+
INPUT:
|
|
390
|
+
|
|
391
|
+
- ``filename`` -- string; the file name. The image format is given by
|
|
392
|
+
the extension, which can be one of the following:
|
|
393
|
+
|
|
394
|
+
* ``.eps``,
|
|
395
|
+
|
|
396
|
+
* ``.pdf``,
|
|
397
|
+
|
|
398
|
+
* ``.png``,
|
|
399
|
+
|
|
400
|
+
* ``.ps``,
|
|
401
|
+
|
|
402
|
+
* ``.sobj`` (for a Sage object you can load later),
|
|
403
|
+
|
|
404
|
+
* ``.svg``,
|
|
405
|
+
|
|
406
|
+
* empty extension will be treated as ``.sobj``.
|
|
407
|
+
|
|
408
|
+
- ``figsize`` -- (default: ``None``) width or [width, height] in inches
|
|
409
|
+
of the Matplotlib figure; if none is provided, Matplotlib's default
|
|
410
|
+
(6.4 x 4.8 inches) is used
|
|
411
|
+
|
|
412
|
+
- ``kwds`` -- keyword arguments, like ``dpi=...``, passed to the
|
|
413
|
+
plotter, see :meth:`show`
|
|
414
|
+
|
|
415
|
+
EXAMPLES::
|
|
416
|
+
|
|
417
|
+
sage: F = tmp_filename(ext='.png')
|
|
418
|
+
sage: L = [plot(sin(k*x), (x,-pi,pi)) for k in [1..3]]
|
|
419
|
+
sage: G = graphics_array(L)
|
|
420
|
+
sage: G.save(F, dpi=500, axes=False)
|
|
421
|
+
|
|
422
|
+
TESTS::
|
|
423
|
+
|
|
424
|
+
sage: graphics_array([]).save(F)
|
|
425
|
+
sage: graphics_array([[]]).save(F)
|
|
426
|
+
"""
|
|
427
|
+
from matplotlib import rcParams
|
|
428
|
+
ext = os.path.splitext(filename)[1].lower()
|
|
429
|
+
if ext in ['', '.sobj']:
|
|
430
|
+
SageObject.save(self, filename)
|
|
431
|
+
elif ext not in ALLOWED_EXTENSIONS:
|
|
432
|
+
raise ValueError("allowed file extensions for images are '" +
|
|
433
|
+
"', '".join(ALLOWED_EXTENSIONS) + "'!")
|
|
434
|
+
else:
|
|
435
|
+
rc_backup = (rcParams['ps.useafm'], rcParams['pdf.use14corefonts'],
|
|
436
|
+
rcParams['text.usetex']) # save the rcParams
|
|
437
|
+
figure = self.matplotlib(figsize=figsize, **kwds)
|
|
438
|
+
transparent = kwds.get('transparent',
|
|
439
|
+
Graphics.SHOW_OPTIONS['transparent'])
|
|
440
|
+
fig_tight = kwds.get('fig_tight',
|
|
441
|
+
Graphics.SHOW_OPTIONS['fig_tight'])
|
|
442
|
+
dpi = kwds.get('dpi', Graphics.SHOW_OPTIONS['dpi'])
|
|
443
|
+
# One can output in PNG, PS, EPS, PDF, PGF, or SVG format,
|
|
444
|
+
# depending on the file extension.
|
|
445
|
+
# PGF is handled by a different backend
|
|
446
|
+
if ext == '.pgf':
|
|
447
|
+
from sage.features.latex import xelatex,pdflatex,lualatex
|
|
448
|
+
latex_implementations = []
|
|
449
|
+
if xelatex().is_present():
|
|
450
|
+
latex_implementations.append('xelatex')
|
|
451
|
+
if pdflatex().is_present():
|
|
452
|
+
latex_implementations.append('pdflatex')
|
|
453
|
+
if lualatex().is_present():
|
|
454
|
+
latex_implementations.append('lualatex')
|
|
455
|
+
if not latex_implementations:
|
|
456
|
+
raise ValueError("Matplotlib requires either xelatex, "
|
|
457
|
+
"lualatex, or pdflatex.")
|
|
458
|
+
if latex_implementations[0] == "pdflatex":
|
|
459
|
+
# use pdflatex and set font encoding as per
|
|
460
|
+
# Matplotlib documentation:
|
|
461
|
+
# https://matplotlib.org/users/pgf.html#pgf-tutorial
|
|
462
|
+
pgf_options = {"pgf.texsystem": "pdflatex",
|
|
463
|
+
"pgf.preamble": [
|
|
464
|
+
r"\usepackage[utf8x]{inputenc}",
|
|
465
|
+
r"\usepackage[T1]{fontenc}"
|
|
466
|
+
]}
|
|
467
|
+
else:
|
|
468
|
+
pgf_options = {"pgf.texsystem": latex_implementations[0]}
|
|
469
|
+
rcParams.update(pgf_options)
|
|
470
|
+
from matplotlib.backends.backend_pgf import FigureCanvasPgf
|
|
471
|
+
figure.set_canvas(FigureCanvasPgf(figure))
|
|
472
|
+
# Matplotlib looks at the file extension to see what the renderer
|
|
473
|
+
# should be. The default is FigureCanvasAgg for PNG's because this
|
|
474
|
+
# is by far the most common type of files rendered, like in the
|
|
475
|
+
# notebook, for example. If the file extension is not '.png', then
|
|
476
|
+
# Matplotlib will handle it.
|
|
477
|
+
else:
|
|
478
|
+
from matplotlib.backends.backend_agg import FigureCanvasAgg
|
|
479
|
+
figure.set_canvas(FigureCanvasAgg(figure))
|
|
480
|
+
if isinstance(self, GraphicsArray):
|
|
481
|
+
# tight_layout adjusts the *subplot* parameters so ticks aren't
|
|
482
|
+
# cut off, etc.
|
|
483
|
+
figure.tight_layout()
|
|
484
|
+
opts = {"dpi": dpi, "transparent": transparent}
|
|
485
|
+
if fig_tight is True:
|
|
486
|
+
opts['bbox_inches'] = 'tight'
|
|
487
|
+
figure.savefig(filename, **opts)
|
|
488
|
+
# Restore the rcParams to the original, possibly user-set values
|
|
489
|
+
(rcParams['ps.useafm'], rcParams['pdf.use14corefonts'],
|
|
490
|
+
rcParams['text.usetex']) = rc_backup
|
|
491
|
+
|
|
492
|
+
def save_image(self, filename=None, *args, **kwds):
|
|
493
|
+
r"""
|
|
494
|
+
Save an image representation of ``self``. The image type is
|
|
495
|
+
determined by the extension of the filename. For example,
|
|
496
|
+
this could be ``.png``, ``.jpg``, ``.gif``, ``.pdf``,
|
|
497
|
+
``.svg``. Currently this is implemented by calling the
|
|
498
|
+
:meth:`save` method of self, passing along all arguments and
|
|
499
|
+
keywords.
|
|
500
|
+
|
|
501
|
+
.. NOTE::
|
|
502
|
+
|
|
503
|
+
Not all image types are necessarily implemented for all
|
|
504
|
+
graphics types. See :meth:`save` for more details.
|
|
505
|
+
|
|
506
|
+
EXAMPLES::
|
|
507
|
+
|
|
508
|
+
sage: plots = [[plot(m*cos(x + n*pi/4), (x, 0, 2*pi))
|
|
509
|
+
....: for n in range(3)] for m in range(1,3)]
|
|
510
|
+
sage: G = graphics_array(plots)
|
|
511
|
+
sage: G.save_image(tmp_filename(ext='.png'))
|
|
512
|
+
"""
|
|
513
|
+
self.save(filename, *args, **kwds)
|
|
514
|
+
|
|
515
|
+
def _latex_(self, **kwds):
|
|
516
|
+
r"""
|
|
517
|
+
Return a string plotting ``self`` with PGF.
|
|
518
|
+
|
|
519
|
+
INPUT:
|
|
520
|
+
|
|
521
|
+
- ``**kwds`` -- all keyword arguments will be passed to the plotter
|
|
522
|
+
|
|
523
|
+
OUTPUT: string of PGF commands to plot ``self``
|
|
524
|
+
|
|
525
|
+
EXAMPLES::
|
|
526
|
+
|
|
527
|
+
sage: A = graphics_array([plot(sin), plot(cos)])
|
|
528
|
+
sage: A._latex_()[:40] # not tested (see comment below)
|
|
529
|
+
'%% Creator: Matplotlib, PGF backend\n%%\n%'
|
|
530
|
+
|
|
531
|
+
The above doctest fails on macOS due to the following Matplotlib issue: https://github.com/matplotlib/matplotlib/issues/10307
|
|
532
|
+
"""
|
|
533
|
+
tmpfilename = tmp_filename(ext='.pgf')
|
|
534
|
+
self.save(filename=tmpfilename, **kwds)
|
|
535
|
+
with open(tmpfilename) as tmpfile:
|
|
536
|
+
latex_list = tmpfile.readlines()
|
|
537
|
+
return ''.join(latex_list)
|
|
538
|
+
|
|
539
|
+
def show(self, **kwds):
|
|
540
|
+
r"""
|
|
541
|
+
Show ``self`` immediately.
|
|
542
|
+
|
|
543
|
+
This method attempts to display the graphics immediately,
|
|
544
|
+
without waiting for the currently running code (if any) to
|
|
545
|
+
return to the command line. Be careful, calling it from within
|
|
546
|
+
a loop will potentially launch a large number of external
|
|
547
|
+
viewer programs.
|
|
548
|
+
|
|
549
|
+
OPTIONAL INPUT:
|
|
550
|
+
|
|
551
|
+
- ``dpi`` -- dots per inch
|
|
552
|
+
|
|
553
|
+
- ``figsize`` -- width or [width, height] of the figure, in inches; the
|
|
554
|
+
default is 6.4 x 4.8 inches
|
|
555
|
+
|
|
556
|
+
- ``axes`` -- boolean; if ``True``, all individual graphics are
|
|
557
|
+
endowed with axes; if ``False``, all axes are removed (this overrides
|
|
558
|
+
the ``axes`` option set in each graphics)
|
|
559
|
+
|
|
560
|
+
- ``frame`` -- boolean; if ``True``, all individual graphics are
|
|
561
|
+
drawn with a frame around them; if ``False``, all frames are removed
|
|
562
|
+
(this overrides the ``frame`` option set in each graphics)
|
|
563
|
+
|
|
564
|
+
- ``fontsize`` -- positive integer, the size of fonts for the axes
|
|
565
|
+
labels (this overrides the ``fontsize`` option set in each graphics)
|
|
566
|
+
|
|
567
|
+
OUTPUT:
|
|
568
|
+
|
|
569
|
+
This method does not return anything. Use :meth:`save` if you
|
|
570
|
+
want to save the figure as an image.
|
|
571
|
+
|
|
572
|
+
EXAMPLES:
|
|
573
|
+
|
|
574
|
+
This draws a graphics array with four trig plots and no axes in any of
|
|
575
|
+
the plots and a figure width of 4 inches::
|
|
576
|
+
|
|
577
|
+
sage: G = graphics_array([[plot(sin), plot(cos)],
|
|
578
|
+
....: [plot(tan), plot(sec)]])
|
|
579
|
+
sage: G.show(axes=False, figsize=4)
|
|
580
|
+
|
|
581
|
+
.. PLOT::
|
|
582
|
+
|
|
583
|
+
G = graphics_array([[plot(sin), plot(cos)], \
|
|
584
|
+
[plot(tan), plot(sec)]])
|
|
585
|
+
sphinx_plot(G, axes=False, figsize=4)
|
|
586
|
+
|
|
587
|
+
Same thing with a frame around each individual graphics::
|
|
588
|
+
|
|
589
|
+
sage: G.show(axes=False, frame=True, figsize=4)
|
|
590
|
+
|
|
591
|
+
.. PLOT::
|
|
592
|
+
|
|
593
|
+
G = graphics_array([[plot(sin), plot(cos)], \
|
|
594
|
+
[plot(tan), plot(sec)]])
|
|
595
|
+
sphinx_plot(G, axes=False, frame=True, figsize=4)
|
|
596
|
+
|
|
597
|
+
Actually, many options are possible; for instance, we may set
|
|
598
|
+
``fontsize`` and ``gridlines``::
|
|
599
|
+
|
|
600
|
+
sage: G.show(axes=False, frame=True, figsize=4, fontsize=8,
|
|
601
|
+
....: gridlines='major')
|
|
602
|
+
|
|
603
|
+
.. PLOT::
|
|
604
|
+
|
|
605
|
+
G = graphics_array([[plot(sin), plot(cos)], \
|
|
606
|
+
[plot(tan), plot(sec)]])
|
|
607
|
+
sphinx_plot(G, axes=False, frame=True, figsize=4, fontsize=8, \
|
|
608
|
+
gridlines='major')
|
|
609
|
+
"""
|
|
610
|
+
from sage.repl.rich_output import get_display_manager
|
|
611
|
+
dm = get_display_manager()
|
|
612
|
+
dm.display_immediately(self, **kwds)
|
|
613
|
+
|
|
614
|
+
def plot(self):
|
|
615
|
+
r"""
|
|
616
|
+
Return ``self`` since ``self`` is already a graphics object.
|
|
617
|
+
|
|
618
|
+
EXAMPLES::
|
|
619
|
+
|
|
620
|
+
sage: g1 = plot(cos, 0, 1)
|
|
621
|
+
sage: g2 = circle((0,0), 1)
|
|
622
|
+
sage: G = multi_graphics([g1, g2])
|
|
623
|
+
sage: G.plot() is G
|
|
624
|
+
True
|
|
625
|
+
"""
|
|
626
|
+
return self
|
|
627
|
+
|
|
628
|
+
def inset(self, graphics, pos=None, fontsize=None):
|
|
629
|
+
r"""
|
|
630
|
+
Add a graphics object as an inset.
|
|
631
|
+
|
|
632
|
+
INPUT:
|
|
633
|
+
|
|
634
|
+
- ``graphics`` -- the graphics object (instance of :class:`Graphics`)
|
|
635
|
+
to be added as an inset
|
|
636
|
+
|
|
637
|
+
- ``pos`` -- (default: ``None``) 4-tuple
|
|
638
|
+
``(left, bottom, width, height)`` specifying the location and
|
|
639
|
+
relative size of the inset on the canvas, all quantities being
|
|
640
|
+
expressed in fractions of the canvas width and height; if ``None``,
|
|
641
|
+
the value ``(0.7, 0.7, 0.2, 0.2)`` is used
|
|
642
|
+
|
|
643
|
+
- ``fontsize`` -- (default: ``None``) integer, font size (in points)
|
|
644
|
+
for the inset; if ``None``, the value of 6 points is used, unless
|
|
645
|
+
``fontsize`` has been explicitly set in the construction of
|
|
646
|
+
``graphics`` (in this case, it is not overwritten here)
|
|
647
|
+
|
|
648
|
+
OUTPUT: instance of :class:`~sage.plot.multigraphics.MultiGraphics`
|
|
649
|
+
|
|
650
|
+
EXAMPLES:
|
|
651
|
+
|
|
652
|
+
Let us consider a graphics array of 2 elements::
|
|
653
|
+
|
|
654
|
+
sage: G = graphics_array([plot(sin, (0, 2*pi)),
|
|
655
|
+
....: plot(cos, (0, 2*pi))])
|
|
656
|
+
sage: G
|
|
657
|
+
Graphics Array of size 1 x 2
|
|
658
|
+
|
|
659
|
+
.. PLOT::
|
|
660
|
+
|
|
661
|
+
G = graphics_array([plot(sin, (0, 2*pi)), plot(cos, (0, 2*pi))])
|
|
662
|
+
sphinx_plot(G)
|
|
663
|
+
|
|
664
|
+
and add some inset at the default position::
|
|
665
|
+
|
|
666
|
+
sage: c = circle((0,0), 1, color='red', thickness=2, frame=True)
|
|
667
|
+
sage: G.inset(c)
|
|
668
|
+
Multigraphics with 3 elements
|
|
669
|
+
|
|
670
|
+
.. PLOT::
|
|
671
|
+
|
|
672
|
+
G = graphics_array([plot(sin, (0, 2*pi)), plot(cos, (0, 2*pi))])
|
|
673
|
+
c = circle((0,0), 1, color='red', thickness=2, frame=True)
|
|
674
|
+
sphinx_plot(G.inset(c))
|
|
675
|
+
|
|
676
|
+
We may customize the position and font size of the inset::
|
|
677
|
+
|
|
678
|
+
sage: G.inset(c, pos=(0.3, 0.7, 0.2, 0.2), fontsize=8)
|
|
679
|
+
Multigraphics with 3 elements
|
|
680
|
+
|
|
681
|
+
.. PLOT::
|
|
682
|
+
|
|
683
|
+
G = graphics_array([plot(sin, (0, 2*pi)), plot(cos, (0, 2*pi))])
|
|
684
|
+
c = circle((0,0), 1, color='red', thickness=2, frame=True)
|
|
685
|
+
sphinx_plot(G.inset(c, pos=(0.3, 0.7, 0.2, 0.2), fontsize=8))
|
|
686
|
+
"""
|
|
687
|
+
if pos is None:
|
|
688
|
+
pos = (0.7, 0.7, 0.2, 0.2)
|
|
689
|
+
if fontsize is not None:
|
|
690
|
+
graphics._extra_kwds['fontsize'] = fontsize
|
|
691
|
+
elif 'fontsize' not in graphics._extra_kwds:
|
|
692
|
+
graphics._extra_kwds['fontsize'] = 6
|
|
693
|
+
current = [] # list of current pairs (graphics, position)
|
|
694
|
+
for i, g in enumerate(self._glist):
|
|
695
|
+
current.append((g, self.position(i)))
|
|
696
|
+
resu = MultiGraphics(current)
|
|
697
|
+
resu.append(graphics, pos=pos)
|
|
698
|
+
return resu
|
|
699
|
+
|
|
700
|
+
#
|
|
701
|
+
# Methods to reimplement in derived classes:
|
|
702
|
+
#
|
|
703
|
+
def __str__(self):
|
|
704
|
+
r"""
|
|
705
|
+
String representation of ``self``.
|
|
706
|
+
|
|
707
|
+
EXAMPLES::
|
|
708
|
+
|
|
709
|
+
sage: from sage.plot.multigraphics import MultiGraphics
|
|
710
|
+
sage: G = MultiGraphics([])
|
|
711
|
+
sage: G.__str__()
|
|
712
|
+
'Multigraphics with 0 element'
|
|
713
|
+
sage: str(G)
|
|
714
|
+
'Multigraphics with 0 element'
|
|
715
|
+
sage: c = circle((0,0), 1)
|
|
716
|
+
sage: G = MultiGraphics([c])
|
|
717
|
+
sage: str(G)
|
|
718
|
+
'Multigraphics with 1 element'
|
|
719
|
+
sage: G = MultiGraphics([c, c, c])
|
|
720
|
+
sage: str(G)
|
|
721
|
+
'Multigraphics with 3 elements'
|
|
722
|
+
"""
|
|
723
|
+
n = len(self._glist)
|
|
724
|
+
if n <= 1:
|
|
725
|
+
return f"Multigraphics with {n} element"
|
|
726
|
+
return f"Multigraphics with {n} elements"
|
|
727
|
+
|
|
728
|
+
def _add_subplot(self, figure, index, **options):
|
|
729
|
+
r"""
|
|
730
|
+
Add a subplot to a given Matplotlib ``Figure``, the position of
|
|
731
|
+
which is governed by a given element of ``self``.
|
|
732
|
+
|
|
733
|
+
This method encapsulates the Matplotlib method ``Figure.add_axes``
|
|
734
|
+
and is intended to be called by :meth:`MultiGraphics.save`.
|
|
735
|
+
|
|
736
|
+
INPUT:
|
|
737
|
+
|
|
738
|
+
- ``figure`` -- a Matplotlib ``Figure`` object
|
|
739
|
+
- ``index`` -- integer specifying the element of ``self``
|
|
740
|
+
- ``options`` -- extra options to be passed to ``Figure.add_axes``
|
|
741
|
+
|
|
742
|
+
OUTPUT: a Matplotlib ``Axes`` object
|
|
743
|
+
|
|
744
|
+
EXAMPLES::
|
|
745
|
+
|
|
746
|
+
sage: g0 = circle((0,0), 1)
|
|
747
|
+
sage: g1 = plot(sin)
|
|
748
|
+
sage: G = multi_graphics([g0, (g1, (0.2, 0.3, 0.4, 0.1))])
|
|
749
|
+
sage: from matplotlib.figure import Figure
|
|
750
|
+
sage: fig = Figure()
|
|
751
|
+
sage: fig
|
|
752
|
+
<Figure size 640x480 with 0 Axes>
|
|
753
|
+
sage: ax0 = G._add_subplot(fig, 0)
|
|
754
|
+
sage: type(ax0)
|
|
755
|
+
<class 'matplotlib.axes._axes.Axes'>
|
|
756
|
+
sage: fig
|
|
757
|
+
<Figure size 640x480 with 1 Axes>
|
|
758
|
+
sage: ax1 = G._add_subplot(fig, 1)
|
|
759
|
+
sage: fig
|
|
760
|
+
<Figure size 640x480 with 2 Axes>
|
|
761
|
+
|
|
762
|
+
TESTS::
|
|
763
|
+
|
|
764
|
+
sage: [ax0, ax1] == fig.get_axes()
|
|
765
|
+
True
|
|
766
|
+
sage: G.position(1)
|
|
767
|
+
(0.2, 0.3, 0.4, 0.1)
|
|
768
|
+
sage: import numpy # to ensure numpy 2.0 compatibility
|
|
769
|
+
sage: if int(numpy.version.short_version[0]) > 1:
|
|
770
|
+
....: _ = numpy.set_printoptions(legacy="1.25")
|
|
771
|
+
sage: ax1.get_position().bounds # tol 1.0e-13
|
|
772
|
+
(0.2, 0.3, 0.4000000000000001, 0.10000000000000003)
|
|
773
|
+
"""
|
|
774
|
+
# Note: using label=str(index) ensures that a new Axes is generated
|
|
775
|
+
# for each element of ``self``, even if some elements share the same
|
|
776
|
+
# positions
|
|
777
|
+
return figure.add_axes(self._positions[index], label=str(index),
|
|
778
|
+
**options)
|
|
779
|
+
|
|
780
|
+
def position(self, index):
|
|
781
|
+
r"""
|
|
782
|
+
Return the position and relative size of an element of ``self`` on the
|
|
783
|
+
canvas.
|
|
784
|
+
|
|
785
|
+
INPUT:
|
|
786
|
+
|
|
787
|
+
- ``index`` -- integer specifying which element of ``self``
|
|
788
|
+
|
|
789
|
+
OUTPUT:
|
|
790
|
+
|
|
791
|
+
- a 4-tuple ``(left, bottom, width, height)`` giving the location and
|
|
792
|
+
relative size of the element on the canvas, all quantities being
|
|
793
|
+
expressed in fractions of the canvas width and height
|
|
794
|
+
|
|
795
|
+
EXAMPLES::
|
|
796
|
+
|
|
797
|
+
sage: g1 = plot(sin(x^2), (x, 0, 4))
|
|
798
|
+
sage: g2 = circle((0,0), 1, rgbcolor='red', fill=True, axes=False)
|
|
799
|
+
sage: G = multi_graphics([g1, (g2, (0.15, 0.2, 0.1, 0.15))])
|
|
800
|
+
sage: G.position(0) # tol 1.0e-13
|
|
801
|
+
(0.125, 0.11, 0.775, 0.77)
|
|
802
|
+
sage: G.position(1) # tol 1.0e-13
|
|
803
|
+
(0.15, 0.2, 0.1, 0.15)
|
|
804
|
+
"""
|
|
805
|
+
return self._positions[index]
|
|
806
|
+
|
|
807
|
+
def append(self, graphics, pos=None):
|
|
808
|
+
r"""
|
|
809
|
+
Append a graphics object to ``self``.
|
|
810
|
+
|
|
811
|
+
INPUT:
|
|
812
|
+
|
|
813
|
+
- ``graphics`` -- the graphics object (instance of :class:`Graphics`)
|
|
814
|
+
to be added to ``self``
|
|
815
|
+
|
|
816
|
+
- ``pos`` -- (default: ``None``) 4-tuple
|
|
817
|
+
``(left, bottom, width, height)`` specifying the location and size
|
|
818
|
+
of ``graphics`` on the canvas, all quantities being in fractions of
|
|
819
|
+
the canvas width and height; if ``None``, ``graphics`` is assumed to
|
|
820
|
+
occupy the whole canvas, except for some padding; this corresponds to
|
|
821
|
+
the default position
|
|
822
|
+
``(left, bottom, width, height) = (0.125, 0.11, 0.775, 0.77)``
|
|
823
|
+
|
|
824
|
+
EXAMPLES:
|
|
825
|
+
|
|
826
|
+
Let us consider a multigraphics with 2 elements::
|
|
827
|
+
|
|
828
|
+
sage: g1 = plot(chebyshev_T(4, x), (x, -1, 1), title='n=4')
|
|
829
|
+
sage: g2 = plot(chebyshev_T(8, x), (x, -1, 1), title='n=8',
|
|
830
|
+
....: color='red')
|
|
831
|
+
sage: G = multi_graphics([(g1, (0.125, 0.2, 0.4, 0.4)),
|
|
832
|
+
....: (g2, (0.55, 0.4, 0.4, 0.4))])
|
|
833
|
+
sage: G
|
|
834
|
+
Multigraphics with 2 elements
|
|
835
|
+
|
|
836
|
+
.. PLOT::
|
|
837
|
+
|
|
838
|
+
g1 = plot(chebyshev_T(4, x), (x, -1, 1), title='n=4')
|
|
839
|
+
g2 = plot(chebyshev_T(8, x), (x, -1, 1), title='n=8', color='red')
|
|
840
|
+
G = multi_graphics([(g1, (0.125, 0.2, 0.4, 0.4)), \
|
|
841
|
+
(g2, (0.55, 0.4, 0.4, 0.4))])
|
|
842
|
+
sphinx_plot(G)
|
|
843
|
+
|
|
844
|
+
We append a third plot to it::
|
|
845
|
+
|
|
846
|
+
sage: g3 = plot(chebyshev_T(16, x), (x, -1, 1), title='n=16',
|
|
847
|
+
....: color='brown')
|
|
848
|
+
sage: G.append(g3, pos=(0.55, 0.11, 0.4, 0.15))
|
|
849
|
+
sage: G
|
|
850
|
+
Multigraphics with 3 elements
|
|
851
|
+
|
|
852
|
+
.. PLOT::
|
|
853
|
+
|
|
854
|
+
g1 = plot(chebyshev_T(4, x), (x, -1, 1), title='n=4')
|
|
855
|
+
g2 = plot(chebyshev_T(8, x), (x, -1, 1), title='n=8', color='red')
|
|
856
|
+
G = multi_graphics([(g1, (0.125, 0.2, 0.4, 0.4)), \
|
|
857
|
+
(g2, (0.55, 0.4, 0.4, 0.4))])
|
|
858
|
+
g3 = plot(chebyshev_T(16, x), (x, -1, 1), title='n=16', \
|
|
859
|
+
color='brown')
|
|
860
|
+
G.append(g3, pos=(0.55, 0.11, 0.4, 0.15))
|
|
861
|
+
sphinx_plot(G)
|
|
862
|
+
|
|
863
|
+
We may use ``append`` to add a title::
|
|
864
|
+
|
|
865
|
+
sage: title = text("Chebyshev polynomials", (0, 0), fontsize=16,
|
|
866
|
+
....: axes=False)
|
|
867
|
+
sage: G.append(title, pos=(0.18, 0.8, 0.7, 0.1))
|
|
868
|
+
sage: G
|
|
869
|
+
Multigraphics with 4 elements
|
|
870
|
+
|
|
871
|
+
.. PLOT::
|
|
872
|
+
|
|
873
|
+
g1 = plot(chebyshev_T(4, x), (x, -1, 1), title='n=4')
|
|
874
|
+
g2 = plot(chebyshev_T(8, x), (x, -1, 1), title='n=8', color='red')
|
|
875
|
+
G = multi_graphics([(g1, (0.125, 0.2, 0.4, 0.4)), \
|
|
876
|
+
(g2, (0.55, 0.4, 0.4, 0.4))])
|
|
877
|
+
g3 = plot(chebyshev_T(16, x), (x, -1, 1), title='n=16', \
|
|
878
|
+
color='brown')
|
|
879
|
+
G.append(g3, pos=(0.55, 0.11, 0.4, 0.15))
|
|
880
|
+
title = text("Chebyshev polynomials", (0, 0), fontsize=16, \
|
|
881
|
+
axes=False)
|
|
882
|
+
G.append(title, pos=(0.18, 0.8, 0.7, 0.1))
|
|
883
|
+
sphinx_plot(G)
|
|
884
|
+
|
|
885
|
+
.. SEEALSO::
|
|
886
|
+
|
|
887
|
+
:meth:`inset`
|
|
888
|
+
"""
|
|
889
|
+
from matplotlib import rcParams
|
|
890
|
+
if not isinstance(graphics, Graphics):
|
|
891
|
+
raise TypeError("a Graphics object is expected, "
|
|
892
|
+
f"not {graphics}")
|
|
893
|
+
if pos is None:
|
|
894
|
+
# Default position:
|
|
895
|
+
left = rcParams['figure.subplot.left']
|
|
896
|
+
bottom = rcParams['figure.subplot.bottom']
|
|
897
|
+
width = rcParams['figure.subplot.right'] - left
|
|
898
|
+
height = rcParams['figure.subplot.top'] - bottom
|
|
899
|
+
pos = (left, bottom, width, height)
|
|
900
|
+
elif not isinstance(pos, (list, tuple)) or len(pos) != 4:
|
|
901
|
+
raise TypeError(f"pos must be a 4-tuple, not {pos}")
|
|
902
|
+
pos = tuple(float(p) for p in pos)
|
|
903
|
+
self._glist.append(graphics)
|
|
904
|
+
self._positions.append(pos)
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
# ****************************************************************************
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
class GraphicsArray(MultiGraphics):
|
|
911
|
+
r"""
|
|
912
|
+
This class implements 2-dimensional graphical objects that constitute
|
|
913
|
+
an array of :class:`~sage.plot.graphics.Graphics` drawn on a single
|
|
914
|
+
canvas.
|
|
915
|
+
|
|
916
|
+
The user interface is through the function
|
|
917
|
+
:func:`~sage.plot.plot.graphics_array`.
|
|
918
|
+
|
|
919
|
+
INPUT:
|
|
920
|
+
|
|
921
|
+
- ``array`` -- either a list of lists of
|
|
922
|
+
:class:`~sage.plot.graphics.Graphics` elements (generic case) or a
|
|
923
|
+
single list of :class:`~sage.plot.graphics.Graphics` elements (case of a
|
|
924
|
+
single-row array)
|
|
925
|
+
|
|
926
|
+
EXAMPLES:
|
|
927
|
+
|
|
928
|
+
An array made of four graphics objects::
|
|
929
|
+
|
|
930
|
+
sage: g1 = plot(sin(x^2), (x, 0, 6), axes_labels=['$x$', '$y$'],
|
|
931
|
+
....: axes=False, frame=True, gridlines='minor')
|
|
932
|
+
sage: y = var('y')
|
|
933
|
+
sage: g2 = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3),
|
|
934
|
+
....: aspect_ratio=1)
|
|
935
|
+
sage: g3 = graphs.DodecahedralGraph().plot()
|
|
936
|
+
sage: g4 = polar_plot(sin(5*x)^2, (x, 0, 2*pi), color='green',
|
|
937
|
+
....: fontsize=8) \
|
|
938
|
+
....: + circle((0,0), 0.5, rgbcolor='red', fill=True, alpha=0.1,
|
|
939
|
+
....: legend_label='pink')
|
|
940
|
+
sage: g4.set_legend_options(loc='upper right')
|
|
941
|
+
sage: G = graphics_array([[g1, g2], [g3, g4]])
|
|
942
|
+
sage: G
|
|
943
|
+
Graphics Array of size 2 x 2
|
|
944
|
+
|
|
945
|
+
.. PLOT::
|
|
946
|
+
|
|
947
|
+
g1 = plot(sin(x**2), (x, 0, 6), axes_labels=['$x$', '$y$'], \
|
|
948
|
+
axes=False, frame=True, gridlines='minor')
|
|
949
|
+
y = var('y')
|
|
950
|
+
g2 = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3), \
|
|
951
|
+
aspect_ratio=1)
|
|
952
|
+
g3 = graphs.DodecahedralGraph().plot()
|
|
953
|
+
g4 = polar_plot(sin(5*x)**2, (x, 0, 2*pi), color='green', fontsize=8) \
|
|
954
|
+
+ circle((0,0), 0.5, rgbcolor='red', fill=True, alpha=0.1, \
|
|
955
|
+
legend_label='pink')
|
|
956
|
+
g4.set_legend_options(loc='upper right')
|
|
957
|
+
G = graphics_array([[g1, g2], [g3, g4]])
|
|
958
|
+
sphinx_plot(G)
|
|
959
|
+
|
|
960
|
+
If one constructs the graphics array from a single list of graphics
|
|
961
|
+
objects, one obtains a single-row array::
|
|
962
|
+
|
|
963
|
+
sage: G = graphics_array([g1, g2, g3, g4])
|
|
964
|
+
sage: G
|
|
965
|
+
Graphics Array of size 1 x 4
|
|
966
|
+
|
|
967
|
+
.. PLOT::
|
|
968
|
+
|
|
969
|
+
g1 = plot(sin(x**2), (x, 0, 6), axes_labels=['$x$', '$y$'], \
|
|
970
|
+
axes=False, frame=True, gridlines='minor')
|
|
971
|
+
y = var('y')
|
|
972
|
+
g2 = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3), \
|
|
973
|
+
aspect_ratio=1)
|
|
974
|
+
g3 = graphs.DodecahedralGraph().plot()
|
|
975
|
+
g4 = polar_plot(sin(5*x)**2, (x, 0, 2*pi), color='green', fontsize=8) \
|
|
976
|
+
+ circle((0,0), 0.5, rgbcolor='red', fill=True, alpha=0.1, \
|
|
977
|
+
legend_label='pink')
|
|
978
|
+
g4.set_legend_options(loc='upper right')
|
|
979
|
+
G = graphics_array([g1, g2, g3, g4])
|
|
980
|
+
sphinx_plot(G)
|
|
981
|
+
|
|
982
|
+
We note that the overall aspect ratio of the figure is 4/3 (the default),
|
|
983
|
+
which makes ``g1`` elongated, while the aspect ratio of ``g2``, which has
|
|
984
|
+
been specified with the parameter ``aspect_ratio=1`` is preserved. To get
|
|
985
|
+
a better aspect ratio for the whole figure, one can use the option
|
|
986
|
+
``figsize`` in the method :meth:`~MultiGraphics.show`::
|
|
987
|
+
|
|
988
|
+
sage: G.show(figsize=[8, 3])
|
|
989
|
+
|
|
990
|
+
.. PLOT::
|
|
991
|
+
|
|
992
|
+
g1 = plot(sin(x**2), (x, 0, 6), axes_labels=['$x$', '$y$'], \
|
|
993
|
+
axes=False, frame=True, gridlines='minor')
|
|
994
|
+
y = var('y')
|
|
995
|
+
g2 = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3), \
|
|
996
|
+
aspect_ratio=1)
|
|
997
|
+
g3 = graphs.DodecahedralGraph().plot()
|
|
998
|
+
g4 = polar_plot(sin(5*x)**2, (x, 0, 2*pi), color='green', fontsize=8) \
|
|
999
|
+
+ circle((0,0), 0.5, rgbcolor='red', fill=True, alpha=0.1, \
|
|
1000
|
+
legend_label='pink')
|
|
1001
|
+
g4.set_legend_options(loc='upper right')
|
|
1002
|
+
G = graphics_array([g1, g2, g3, g4])
|
|
1003
|
+
sphinx_plot(G, figsize=[8, 3])
|
|
1004
|
+
|
|
1005
|
+
We can access individual elements of the graphics array with the
|
|
1006
|
+
square-bracket operator::
|
|
1007
|
+
|
|
1008
|
+
sage: G = graphics_array([[g1, g2], [g3, g4]]) # back to the 2x2 array
|
|
1009
|
+
sage: print(G)
|
|
1010
|
+
Graphics Array of size 2 x 2
|
|
1011
|
+
sage: G[0] is g1
|
|
1012
|
+
True
|
|
1013
|
+
sage: G[1] is g2
|
|
1014
|
+
True
|
|
1015
|
+
sage: G[2] is g3
|
|
1016
|
+
True
|
|
1017
|
+
sage: G[3] is g4
|
|
1018
|
+
True
|
|
1019
|
+
|
|
1020
|
+
Note that with respect to the square-bracket operator, ``G`` is considered
|
|
1021
|
+
as a flattened list of graphics objects, not as an array. For instance,
|
|
1022
|
+
``G[0, 1]`` throws an error::
|
|
1023
|
+
|
|
1024
|
+
sage: G[0, 1]
|
|
1025
|
+
Traceback (most recent call last):
|
|
1026
|
+
...
|
|
1027
|
+
TypeError: list indices must be integers or slices, not tuple
|
|
1028
|
+
|
|
1029
|
+
``G[:]`` returns the full (flattened) list of graphics objects composing
|
|
1030
|
+
``G``::
|
|
1031
|
+
|
|
1032
|
+
sage: G[:]
|
|
1033
|
+
[Graphics object consisting of 1 graphics primitive,
|
|
1034
|
+
Graphics object consisting of 1 graphics primitive,
|
|
1035
|
+
Graphics object consisting of 51 graphics primitives,
|
|
1036
|
+
Graphics object consisting of 2 graphics primitives]
|
|
1037
|
+
|
|
1038
|
+
The total number of Graphics objects composing the array is returned
|
|
1039
|
+
by the function ``len``::
|
|
1040
|
+
|
|
1041
|
+
sage: len(G)
|
|
1042
|
+
4
|
|
1043
|
+
|
|
1044
|
+
The square-bracket operator can be used to replace elements in the array::
|
|
1045
|
+
|
|
1046
|
+
sage: G[0] = g4
|
|
1047
|
+
sage: G
|
|
1048
|
+
Graphics Array of size 2 x 2
|
|
1049
|
+
|
|
1050
|
+
.. PLOT::
|
|
1051
|
+
|
|
1052
|
+
g1 = plot(sin(x**2), (x, 0, 6), axes_labels=['$x$', '$y$'], \
|
|
1053
|
+
axes=False, frame=True, gridlines='minor')
|
|
1054
|
+
y = var('y')
|
|
1055
|
+
g2 = streamline_plot((sin(x), cos(y)), (x,-3,3), (y,-3,3), \
|
|
1056
|
+
aspect_ratio=1)
|
|
1057
|
+
g3 = graphs.DodecahedralGraph().plot()
|
|
1058
|
+
g4 = polar_plot(sin(5*x)**2, (x, 0, 2*pi), color='green', fontsize=8) \
|
|
1059
|
+
+ circle((0,0), 0.5, rgbcolor='red', fill=True, alpha=0.1, \
|
|
1060
|
+
legend_label='pink')
|
|
1061
|
+
g4.set_legend_options(loc='upper right')
|
|
1062
|
+
G = graphics_array([[g1, g2], [g3, g4]])
|
|
1063
|
+
G[0] = g4
|
|
1064
|
+
sphinx_plot(G)
|
|
1065
|
+
"""
|
|
1066
|
+
def __init__(self, array):
|
|
1067
|
+
r"""
|
|
1068
|
+
Construct a ``GraphicsArray``.
|
|
1069
|
+
|
|
1070
|
+
TESTS::
|
|
1071
|
+
|
|
1072
|
+
sage: from sage.plot.multigraphics import GraphicsArray
|
|
1073
|
+
sage: g = circle((0,0), 1) # a Graphics object
|
|
1074
|
+
sage: G = GraphicsArray([[g, g], [g, g]])
|
|
1075
|
+
sage: print(G)
|
|
1076
|
+
Graphics Array of size 2 x 2
|
|
1077
|
+
|
|
1078
|
+
Construction from a single list ==> 1-row array::
|
|
1079
|
+
|
|
1080
|
+
sage: G = GraphicsArray([g, g, g])
|
|
1081
|
+
sage: print(G)
|
|
1082
|
+
Graphics Array of size 1 x 3
|
|
1083
|
+
sage: G = GraphicsArray([g])
|
|
1084
|
+
sage: print(G)
|
|
1085
|
+
Graphics Array of size 1 x 1
|
|
1086
|
+
|
|
1087
|
+
Empty array::
|
|
1088
|
+
|
|
1089
|
+
sage: G = GraphicsArray([])
|
|
1090
|
+
sage: print(G)
|
|
1091
|
+
Graphics Array of size 0 x 0
|
|
1092
|
+
sage: len(G)
|
|
1093
|
+
0
|
|
1094
|
+
sage: G = GraphicsArray([[]])
|
|
1095
|
+
sage: print(G)
|
|
1096
|
+
Graphics Array of size 1 x 0
|
|
1097
|
+
sage: len(G)
|
|
1098
|
+
0
|
|
1099
|
+
|
|
1100
|
+
Check treatment of wrong inputs::
|
|
1101
|
+
|
|
1102
|
+
sage: G = GraphicsArray([[g, g], [g]])
|
|
1103
|
+
Traceback (most recent call last):
|
|
1104
|
+
...
|
|
1105
|
+
TypeError: array must be a list of equal-size lists of Graphics
|
|
1106
|
+
objects, not [[Graphics object consisting of 1 graphics primitive,
|
|
1107
|
+
Graphics object consisting of 1 graphics primitive],
|
|
1108
|
+
[Graphics object consisting of 1 graphics primitive]]
|
|
1109
|
+
sage: G = GraphicsArray(g)
|
|
1110
|
+
Traceback (most recent call last):
|
|
1111
|
+
...
|
|
1112
|
+
TypeError: array must be a list of lists of Graphics objects, not
|
|
1113
|
+
Graphics object consisting of 1 graphics primitive
|
|
1114
|
+
sage: G = GraphicsArray([g, x])
|
|
1115
|
+
Traceback (most recent call last):
|
|
1116
|
+
...
|
|
1117
|
+
TypeError: every element of array must be a Graphics object
|
|
1118
|
+
"""
|
|
1119
|
+
MultiGraphics.__init__(self, [])
|
|
1120
|
+
if not isinstance(array, (list, tuple)):
|
|
1121
|
+
raise TypeError("array must be a list of lists of Graphics "
|
|
1122
|
+
f"objects, not {array}")
|
|
1123
|
+
array = list(array)
|
|
1124
|
+
self._rows = len(array)
|
|
1125
|
+
if self._rows > 0:
|
|
1126
|
+
if not isinstance(array[0], (list, tuple)):
|
|
1127
|
+
array = [array]
|
|
1128
|
+
self._rows = 1
|
|
1129
|
+
self._cols = len(array[0])
|
|
1130
|
+
else:
|
|
1131
|
+
self._cols = 0
|
|
1132
|
+
for row in array: # basically flatten the list
|
|
1133
|
+
if not isinstance(row, (list, tuple)) or len(row) != self._cols:
|
|
1134
|
+
raise TypeError("array must be a list of equal-size lists of "
|
|
1135
|
+
f"Graphics objects, not {array}")
|
|
1136
|
+
for g in row:
|
|
1137
|
+
if not isinstance(g, Graphics):
|
|
1138
|
+
raise TypeError("every element of array must be a "
|
|
1139
|
+
"Graphics object")
|
|
1140
|
+
self._glist.append(g)
|
|
1141
|
+
# self._positions is not initialized since most of the time, it is not
|
|
1142
|
+
# not used. It is required only by the method inset(); it is then
|
|
1143
|
+
# initialized by the method position().
|
|
1144
|
+
|
|
1145
|
+
def __str__(self):
|
|
1146
|
+
r"""
|
|
1147
|
+
String representation of the graphics array.
|
|
1148
|
+
|
|
1149
|
+
EXAMPLES::
|
|
1150
|
+
|
|
1151
|
+
sage: c = circle((0,0), 1)
|
|
1152
|
+
sage: G = graphics_array([c]*6, 2, 3)
|
|
1153
|
+
sage: G.__str__()
|
|
1154
|
+
'Graphics Array of size 2 x 3'
|
|
1155
|
+
sage: str(G)
|
|
1156
|
+
'Graphics Array of size 2 x 3'
|
|
1157
|
+
"""
|
|
1158
|
+
return f"Graphics Array of size {self._rows} x {self._cols}"
|
|
1159
|
+
|
|
1160
|
+
def _add_subplot(self, figure, index, **options):
|
|
1161
|
+
r"""
|
|
1162
|
+
Add a subplot to a given Matplotlib ``Figure``, the position of
|
|
1163
|
+
which is governed by a given element of ``self``.
|
|
1164
|
+
|
|
1165
|
+
This method encapsulates the Matplotlib method ``Figure.add_subplot``
|
|
1166
|
+
and is intended to be called by :meth:`MultiGraphics.save`.
|
|
1167
|
+
|
|
1168
|
+
INPUT:
|
|
1169
|
+
|
|
1170
|
+
- ``figure`` -- a Matplotlib ``Figure`` object
|
|
1171
|
+
- ``index`` -- integer specifying the element of ``self``
|
|
1172
|
+
- ``options`` -- extra options to be passed to ``Figure.add_subplot``
|
|
1173
|
+
|
|
1174
|
+
OUTPUT: a Matplotlib ``Axes`` object
|
|
1175
|
+
|
|
1176
|
+
EXAMPLES::
|
|
1177
|
+
|
|
1178
|
+
sage: c = circle((0,0), 1)
|
|
1179
|
+
sage: G = graphics_array([c, c])
|
|
1180
|
+
sage: from matplotlib.figure import Figure
|
|
1181
|
+
sage: fig = Figure()
|
|
1182
|
+
sage: ax1 = G._add_subplot(fig, 0)
|
|
1183
|
+
sage: type(ax1)
|
|
1184
|
+
<class 'matplotlib.axes...'>
|
|
1185
|
+
sage: ax2 = G._add_subplot(fig, 1)
|
|
1186
|
+
sage: fig.get_axes() == [ax1, ax2]
|
|
1187
|
+
True
|
|
1188
|
+
"""
|
|
1189
|
+
if self._rows == 0 or self._cols == 0:
|
|
1190
|
+
rows = 1
|
|
1191
|
+
cols = 1
|
|
1192
|
+
else:
|
|
1193
|
+
rows = self._rows
|
|
1194
|
+
cols = self._cols
|
|
1195
|
+
# index --> index + 1 for Figure.add_subplot:
|
|
1196
|
+
return figure.add_subplot(rows, cols, index + 1, **options)
|
|
1197
|
+
|
|
1198
|
+
def nrows(self):
|
|
1199
|
+
r"""
|
|
1200
|
+
Number of rows of the graphics array.
|
|
1201
|
+
|
|
1202
|
+
EXAMPLES::
|
|
1203
|
+
|
|
1204
|
+
sage: R = rainbow(6)
|
|
1205
|
+
sage: L = [plot(x^n, (x,0,1), color=R[n]) for n in range(6)]
|
|
1206
|
+
sage: G = graphics_array(L, 2, 3)
|
|
1207
|
+
sage: G.nrows()
|
|
1208
|
+
2
|
|
1209
|
+
sage: graphics_array(L).nrows()
|
|
1210
|
+
1
|
|
1211
|
+
"""
|
|
1212
|
+
return self._rows
|
|
1213
|
+
|
|
1214
|
+
def ncols(self):
|
|
1215
|
+
r"""
|
|
1216
|
+
Number of columns of the graphics array.
|
|
1217
|
+
|
|
1218
|
+
EXAMPLES::
|
|
1219
|
+
|
|
1220
|
+
sage: R = rainbow(6)
|
|
1221
|
+
sage: L = [plot(x^n, (x,0,1), color=R[n]) for n in range(6)]
|
|
1222
|
+
sage: G = graphics_array(L, 2, 3)
|
|
1223
|
+
sage: G.ncols()
|
|
1224
|
+
3
|
|
1225
|
+
sage: graphics_array(L).ncols()
|
|
1226
|
+
6
|
|
1227
|
+
"""
|
|
1228
|
+
return self._cols
|
|
1229
|
+
|
|
1230
|
+
def append(self, g):
|
|
1231
|
+
r"""
|
|
1232
|
+
Append a graphics to the array.
|
|
1233
|
+
|
|
1234
|
+
Currently not implemented.
|
|
1235
|
+
|
|
1236
|
+
TESTS::
|
|
1237
|
+
|
|
1238
|
+
sage: from sage.plot.multigraphics import GraphicsArray
|
|
1239
|
+
sage: c = circle((0,0), 1)
|
|
1240
|
+
sage: G = GraphicsArray([c, c])
|
|
1241
|
+
sage: G.append(c)
|
|
1242
|
+
Traceback (most recent call last):
|
|
1243
|
+
...
|
|
1244
|
+
NotImplementedError: Appending to a graphics array is not yet
|
|
1245
|
+
implemented
|
|
1246
|
+
"""
|
|
1247
|
+
# Not clear if there is a way to do this
|
|
1248
|
+
raise NotImplementedError('Appending to a graphics array is not '
|
|
1249
|
+
'yet implemented')
|
|
1250
|
+
|
|
1251
|
+
def position(self, index):
|
|
1252
|
+
r"""
|
|
1253
|
+
Return the position and relative size of an element of ``self`` on the
|
|
1254
|
+
canvas.
|
|
1255
|
+
|
|
1256
|
+
INPUT:
|
|
1257
|
+
|
|
1258
|
+
- ``index`` -- integer specifying which element of ``self``
|
|
1259
|
+
|
|
1260
|
+
OUTPUT:
|
|
1261
|
+
|
|
1262
|
+
- a 4-tuple ``(left, bottom, width, height)`` giving the location and
|
|
1263
|
+
relative size of the element on the canvas, all quantities being
|
|
1264
|
+
expressed in fractions of the canvas width and height
|
|
1265
|
+
|
|
1266
|
+
EXAMPLES::
|
|
1267
|
+
|
|
1268
|
+
sage: g1 = plot(sin(x), (x, -pi, pi))
|
|
1269
|
+
sage: g2 = circle((0,1), 1.)
|
|
1270
|
+
sage: G = graphics_array([g1, g2])
|
|
1271
|
+
sage: import numpy # to ensure numpy 2.0 compatibility
|
|
1272
|
+
sage: if int(numpy.version.short_version[0]) > 1:
|
|
1273
|
+
....: _ = numpy.set_printoptions(legacy="1.25")
|
|
1274
|
+
sage: G.position(0) # tol 5.0e-3
|
|
1275
|
+
(0.025045451349937315,
|
|
1276
|
+
0.03415488992713045,
|
|
1277
|
+
0.4489880779745068,
|
|
1278
|
+
0.9345951100728696)
|
|
1279
|
+
sage: G.position(1) # tol 5.0e-3
|
|
1280
|
+
(0.5170637412999687,
|
|
1281
|
+
0.20212705964722733,
|
|
1282
|
+
0.4489880779745068,
|
|
1283
|
+
0.5986507706326758)
|
|
1284
|
+
"""
|
|
1285
|
+
if not self._positions:
|
|
1286
|
+
# self._positions must be generated, by invoking get_position() on
|
|
1287
|
+
# each of the Axes of the Matplotlib figure corresponding to self:
|
|
1288
|
+
from matplotlib.backends.backend_agg import FigureCanvasAgg
|
|
1289
|
+
figure = self.matplotlib()
|
|
1290
|
+
figure.set_canvas(FigureCanvasAgg(figure))
|
|
1291
|
+
figure.tight_layout()
|
|
1292
|
+
axes = figure.get_axes()
|
|
1293
|
+
self._positions = [ax.get_position().bounds for ax in axes]
|
|
1294
|
+
return self._positions[index]
|