passagemath-plot 10.6.31rc3__cp314-cp314-macosx_13_0_arm64.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 +82 -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.dylibs/libgfortran.5.dylib +0 -0
- passagemath_plot.dylibs/libgsl.28.dylib +0 -0
- passagemath_plot.dylibs/libopenblasp-r0.3.29.dylib +0 -0
- passagemath_plot.dylibs/libquadmath.0.dylib +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-darwin.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-darwin.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-darwin.so +0 -0
- sage/plot/plot3d/implicit_surface.pyx +1453 -0
- sage/plot/plot3d/index_face_set.cpython-314-darwin.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-darwin.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-darwin.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-darwin.so +0 -0
- sage/plot/plot3d/transform.pxd +21 -0
- sage/plot/plot3d/transform.pyx +268 -0
- sage/plot/plot3d/tri_plot.py +589 -0
- sage/plot/plot_field.py +362 -0
- sage/plot/point.py +624 -0
- sage/plot/polygon.py +562 -0
- sage/plot/primitive.py +249 -0
- sage/plot/scatter_plot.py +199 -0
- sage/plot/step.py +85 -0
- sage/plot/streamline_plot.py +328 -0
- sage/plot/text.py +432 -0
sage/plot/arrow.py
ADDED
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-plot
|
|
2
|
+
"""
|
|
3
|
+
Arrows
|
|
4
|
+
"""
|
|
5
|
+
# ***************************************************************************
|
|
6
|
+
# Copyright (C) 2006 Alex Clemesha <clemesha@gmail.com>,
|
|
7
|
+
# William Stein <wstein@gmail.com>,
|
|
8
|
+
# 2008 Mike Hansen <mhansen@gmail.com>,
|
|
9
|
+
# 2009 Emily Kirkman
|
|
10
|
+
#
|
|
11
|
+
# Distributed under the terms of the GNU General Public License (GPL)
|
|
12
|
+
#
|
|
13
|
+
# This code is distributed in the hope that it will be useful,
|
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
16
|
+
# General Public License for more details.
|
|
17
|
+
#
|
|
18
|
+
# The full text of the GPL is available at:
|
|
19
|
+
#
|
|
20
|
+
# https://www.gnu.org/licenses/
|
|
21
|
+
# ***************************************************************************
|
|
22
|
+
from sage.plot.primitive import GraphicPrimitive
|
|
23
|
+
from sage.misc.decorators import options, rename_keyword
|
|
24
|
+
from sage.plot.colors import to_mpl_color
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class CurveArrow(GraphicPrimitive):
|
|
28
|
+
def __init__(self, path, options):
|
|
29
|
+
"""
|
|
30
|
+
Return an arrow graphics primitive along the provided path (bezier curve).
|
|
31
|
+
|
|
32
|
+
EXAMPLES::
|
|
33
|
+
|
|
34
|
+
sage: from sage.plot.arrow import CurveArrow
|
|
35
|
+
sage: b = CurveArrow(path=[[(0,0),(.5,.5),(1,0)],[(.5,1),(0,0)]],
|
|
36
|
+
....: options={})
|
|
37
|
+
sage: b
|
|
38
|
+
CurveArrow from (0, 0) to (0, 0)
|
|
39
|
+
"""
|
|
40
|
+
import numpy as np
|
|
41
|
+
self.path = path
|
|
42
|
+
codes = [1] + (len(self.path[0])-1)*[len(self.path[0])]
|
|
43
|
+
vertices = self.path[0]
|
|
44
|
+
for curve in self.path[1:]:
|
|
45
|
+
vertices += curve
|
|
46
|
+
codes += (len(curve))*[len(curve)+1]
|
|
47
|
+
self.codes = codes
|
|
48
|
+
self.vertices = np.array(vertices, float)
|
|
49
|
+
GraphicPrimitive.__init__(self, options)
|
|
50
|
+
|
|
51
|
+
def get_minmax_data(self):
|
|
52
|
+
"""
|
|
53
|
+
Return a dictionary with the bounding box data.
|
|
54
|
+
|
|
55
|
+
EXAMPLES::
|
|
56
|
+
|
|
57
|
+
sage: import numpy # to ensure numpy 2.0 compatibility
|
|
58
|
+
sage: if int(numpy.version.short_version[0]) > 1:
|
|
59
|
+
....: _ = numpy.set_printoptions(legacy="1.25")
|
|
60
|
+
sage: from sage.plot.arrow import CurveArrow
|
|
61
|
+
sage: b = CurveArrow(path=[[(0,0),(.5,.5),(1,0)],[(.5,1),(0,0)]],
|
|
62
|
+
....: options={})
|
|
63
|
+
sage: d = b.get_minmax_data()
|
|
64
|
+
sage: d['xmin']
|
|
65
|
+
0.0
|
|
66
|
+
sage: d['xmax']
|
|
67
|
+
1.0
|
|
68
|
+
"""
|
|
69
|
+
return {'xmin': self.vertices[:,0].min(),
|
|
70
|
+
'xmax': self.vertices[:,0].max(),
|
|
71
|
+
'ymin': self.vertices[:,1].min(),
|
|
72
|
+
'ymax': self.vertices[:,1].max()}
|
|
73
|
+
|
|
74
|
+
def _allowed_options(self):
|
|
75
|
+
"""
|
|
76
|
+
Return the dictionary of allowed options for the curve arrow graphics
|
|
77
|
+
primitive.
|
|
78
|
+
|
|
79
|
+
EXAMPLES::
|
|
80
|
+
|
|
81
|
+
sage: from sage.plot.arrow import CurveArrow
|
|
82
|
+
sage: list(sorted(CurveArrow(path=[[(0,0),(2,3)]],options={})._allowed_options().items()))
|
|
83
|
+
[('arrowsize', 'The size of the arrowhead'),
|
|
84
|
+
('arrowstyle', 'todo'),
|
|
85
|
+
('head', '2-d only: Which end of the path to draw the head (one of 0 (start), 1 (end) or 2 (both)'),
|
|
86
|
+
('hue', 'The color given as a hue.'),
|
|
87
|
+
('legend_color', 'The color of the legend text.'),
|
|
88
|
+
('legend_label', 'The label for this item in the legend.'),
|
|
89
|
+
('linestyle', "2d only: The style of the line, which is one of
|
|
90
|
+
'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.',
|
|
91
|
+
respectively."),
|
|
92
|
+
('rgbcolor', 'The color as an RGB tuple.'),
|
|
93
|
+
('thickness', 'The thickness of the arrow.'),
|
|
94
|
+
('width', 'The width of the shaft of the arrow, in points.'),
|
|
95
|
+
('zorder', '2-d only: The layer level in which to draw')]
|
|
96
|
+
"""
|
|
97
|
+
return {'width': 'The width of the shaft of the arrow, in points.',
|
|
98
|
+
'rgbcolor': 'The color as an RGB tuple.',
|
|
99
|
+
'hue': 'The color given as a hue.',
|
|
100
|
+
'legend_label': 'The label for this item in the legend.',
|
|
101
|
+
'legend_color': 'The color of the legend text.',
|
|
102
|
+
'arrowstyle': 'todo',
|
|
103
|
+
'arrowsize': 'The size of the arrowhead',
|
|
104
|
+
'thickness': 'The thickness of the arrow.',
|
|
105
|
+
'zorder': '2-d only: The layer level in which to draw',
|
|
106
|
+
'head': '2-d only: Which end of the path to draw the head (one of 0 (start), 1 (end) or 2 (both)',
|
|
107
|
+
'linestyle': "2d only: The style of the line, which is one of "
|
|
108
|
+
"'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', "
|
|
109
|
+
"respectively."}
|
|
110
|
+
|
|
111
|
+
def _repr_(self):
|
|
112
|
+
"""
|
|
113
|
+
Text representation of an arrow graphics primitive.
|
|
114
|
+
|
|
115
|
+
EXAMPLES::
|
|
116
|
+
|
|
117
|
+
sage: from sage.plot.arrow import CurveArrow
|
|
118
|
+
sage: CurveArrow(path=[[(0,0),(1,4),(2,3)]],options={})._repr_()
|
|
119
|
+
'CurveArrow from (0, 0) to (2, 3)'
|
|
120
|
+
"""
|
|
121
|
+
return f"CurveArrow from {self.path[0][0]} to {self.path[-1][-1]}"
|
|
122
|
+
|
|
123
|
+
def _render_on_subplot(self, subplot):
|
|
124
|
+
"""
|
|
125
|
+
Render this arrow in a subplot.
|
|
126
|
+
|
|
127
|
+
This is the key function that defines how this arrow graphics
|
|
128
|
+
primitive is rendered in matplotlib's library.
|
|
129
|
+
|
|
130
|
+
EXAMPLES:
|
|
131
|
+
|
|
132
|
+
This function implicitly ends up rendering this arrow on a matplotlib
|
|
133
|
+
subplot::
|
|
134
|
+
|
|
135
|
+
sage: arrow(path=[[(0,1), (2,-1), (4,5)]])
|
|
136
|
+
Graphics object consisting of 1 graphics primitive
|
|
137
|
+
"""
|
|
138
|
+
from sage.plot.misc import get_matplotlib_linestyle
|
|
139
|
+
|
|
140
|
+
options = self.options()
|
|
141
|
+
width = float(options['width'])
|
|
142
|
+
head = options.pop('head')
|
|
143
|
+
if head == 0:
|
|
144
|
+
style = '<|-'
|
|
145
|
+
elif head == 1:
|
|
146
|
+
style = '-|>'
|
|
147
|
+
elif head == 2:
|
|
148
|
+
style = '<|-|>'
|
|
149
|
+
else:
|
|
150
|
+
raise KeyError('head parameter must be one of 0 (start), 1 (end) or 2 (both)')
|
|
151
|
+
arrowsize = float(options.get('arrowsize', 5))
|
|
152
|
+
head_width = arrowsize
|
|
153
|
+
head_length = arrowsize * 2.0
|
|
154
|
+
color = to_mpl_color(options['rgbcolor'])
|
|
155
|
+
from matplotlib.patches import FancyArrowPatch
|
|
156
|
+
from matplotlib.path import Path
|
|
157
|
+
bpath = Path(self.vertices, self.codes)
|
|
158
|
+
p = FancyArrowPatch(path=bpath,
|
|
159
|
+
lw=width, arrowstyle='{},head_width={},head_length={}'.format(style, head_width, head_length),
|
|
160
|
+
fc=color, ec=color,
|
|
161
|
+
linestyle=get_matplotlib_linestyle(options['linestyle'], return_type='long'))
|
|
162
|
+
p.set_zorder(options['zorder'])
|
|
163
|
+
p.set_label(options['legend_label'])
|
|
164
|
+
subplot.add_patch(p)
|
|
165
|
+
return p
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class Arrow(GraphicPrimitive):
|
|
169
|
+
"""
|
|
170
|
+
Primitive class that initializes the (line) arrow graphics type.
|
|
171
|
+
|
|
172
|
+
EXAMPLES:
|
|
173
|
+
|
|
174
|
+
We create an arrow graphics object, then take the 0th entry
|
|
175
|
+
in it to get the actual Arrow graphics primitive::
|
|
176
|
+
|
|
177
|
+
sage: P = arrow((0,1), (2,3))[0]
|
|
178
|
+
sage: type(P)
|
|
179
|
+
<class 'sage.plot.arrow.Arrow'>
|
|
180
|
+
sage: P
|
|
181
|
+
Arrow from (0.0,1.0) to (2.0,3.0)
|
|
182
|
+
"""
|
|
183
|
+
def __init__(self, xtail, ytail, xhead, yhead, options):
|
|
184
|
+
"""
|
|
185
|
+
Create an arrow graphics primitive.
|
|
186
|
+
|
|
187
|
+
EXAMPLES::
|
|
188
|
+
|
|
189
|
+
sage: from sage.plot.arrow import Arrow
|
|
190
|
+
sage: Arrow(0,0,2,3,{})
|
|
191
|
+
Arrow from (0.0,0.0) to (2.0,3.0)
|
|
192
|
+
"""
|
|
193
|
+
self.xtail = float(xtail)
|
|
194
|
+
self.xhead = float(xhead)
|
|
195
|
+
self.ytail = float(ytail)
|
|
196
|
+
self.yhead = float(yhead)
|
|
197
|
+
GraphicPrimitive.__init__(self, options)
|
|
198
|
+
|
|
199
|
+
def get_minmax_data(self):
|
|
200
|
+
"""
|
|
201
|
+
Return a bounding box for this arrow.
|
|
202
|
+
|
|
203
|
+
EXAMPLES::
|
|
204
|
+
|
|
205
|
+
sage: d = arrow((1,1), (5,5)).get_minmax_data()
|
|
206
|
+
sage: d['xmin']
|
|
207
|
+
1.0
|
|
208
|
+
sage: d['xmax']
|
|
209
|
+
5.0
|
|
210
|
+
"""
|
|
211
|
+
return {'xmin': min(self.xtail, self.xhead),
|
|
212
|
+
'xmax': max(self.xtail, self.xhead),
|
|
213
|
+
'ymin': min(self.ytail, self.yhead),
|
|
214
|
+
'ymax': max(self.ytail, self.yhead)}
|
|
215
|
+
|
|
216
|
+
def _allowed_options(self):
|
|
217
|
+
"""
|
|
218
|
+
Return the dictionary of allowed options for the line arrow graphics
|
|
219
|
+
primitive.
|
|
220
|
+
|
|
221
|
+
EXAMPLES::
|
|
222
|
+
|
|
223
|
+
sage: from sage.plot.arrow import Arrow
|
|
224
|
+
sage: list(sorted(Arrow(0,0,2,3,{})._allowed_options().items()))
|
|
225
|
+
[('arrowshorten', 'The length in points to shorten the arrow.'),
|
|
226
|
+
('arrowsize', 'The size of the arrowhead'),
|
|
227
|
+
('head',
|
|
228
|
+
'2-d only: Which end of the path to draw the head (one of 0 (start), 1 (end) or 2 (both)'),
|
|
229
|
+
('hue', 'The color given as a hue.'),
|
|
230
|
+
('legend_color', 'The color of the legend text.'),
|
|
231
|
+
('legend_label', 'The label for this item in the legend.'),
|
|
232
|
+
('linestyle',
|
|
233
|
+
"2d only: The style of the line, which is one of 'dashed',
|
|
234
|
+
'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.',
|
|
235
|
+
respectively."),
|
|
236
|
+
('rgbcolor', 'The color as an RGB tuple.'),
|
|
237
|
+
('thickness', 'The thickness of the arrow.'),
|
|
238
|
+
('width', 'The width of the shaft of the arrow, in points.'),
|
|
239
|
+
('zorder', '2-d only: The layer level in which to draw')]
|
|
240
|
+
"""
|
|
241
|
+
return {'width': 'The width of the shaft of the arrow, in points.',
|
|
242
|
+
'rgbcolor': 'The color as an RGB tuple.',
|
|
243
|
+
'hue': 'The color given as a hue.',
|
|
244
|
+
'arrowshorten': 'The length in points to shorten the arrow.',
|
|
245
|
+
'arrowsize': 'The size of the arrowhead',
|
|
246
|
+
'thickness': 'The thickness of the arrow.',
|
|
247
|
+
'legend_label': 'The label for this item in the legend.',
|
|
248
|
+
'legend_color': 'The color of the legend text.',
|
|
249
|
+
'zorder': '2-d only: The layer level in which to draw',
|
|
250
|
+
'head': '2-d only: Which end of the path to draw the head (one of 0 (start), 1 (end) or 2 (both)',
|
|
251
|
+
'linestyle': "2d only: The style of the line, which is one of "
|
|
252
|
+
"'dashed', 'dotted', 'solid', 'dashdot', or '--', ':', '-', '-.', "
|
|
253
|
+
"respectively."}
|
|
254
|
+
|
|
255
|
+
def _plot3d_options(self, options=None):
|
|
256
|
+
"""
|
|
257
|
+
Translate 2D plot options into 3D plot options.
|
|
258
|
+
|
|
259
|
+
EXAMPLES::
|
|
260
|
+
|
|
261
|
+
sage: P = arrow((0,1), (2,3), width=5)
|
|
262
|
+
sage: p=P[0]; p
|
|
263
|
+
Arrow from (0.0,1.0) to (2.0,3.0)
|
|
264
|
+
sage: q=p.plot3d()
|
|
265
|
+
sage: q.thickness
|
|
266
|
+
5
|
|
267
|
+
"""
|
|
268
|
+
if options is None:
|
|
269
|
+
options = self.options()
|
|
270
|
+
options = dict(self.options())
|
|
271
|
+
options_3d = {}
|
|
272
|
+
if 'width' in options:
|
|
273
|
+
options_3d['thickness'] = options['width']
|
|
274
|
+
del options['width']
|
|
275
|
+
# ignore zorder and head in 3d plotting
|
|
276
|
+
if 'zorder' in options:
|
|
277
|
+
del options['zorder']
|
|
278
|
+
if 'head' in options:
|
|
279
|
+
del options['head']
|
|
280
|
+
if 'linestyle' in options:
|
|
281
|
+
del options['linestyle']
|
|
282
|
+
options_3d.update(GraphicPrimitive._plot3d_options(self, options))
|
|
283
|
+
return options_3d
|
|
284
|
+
|
|
285
|
+
def plot3d(self, ztail=0, zhead=0, **kwds):
|
|
286
|
+
"""
|
|
287
|
+
Take 2D plot and places it in 3D.
|
|
288
|
+
|
|
289
|
+
EXAMPLES::
|
|
290
|
+
|
|
291
|
+
sage: A = arrow((0,0),(1,1))[0].plot3d()
|
|
292
|
+
sage: A.jmol_repr(A.testing_render_params())[0]
|
|
293
|
+
'draw line_1 diameter 2 arrow {0.0 0.0 0.0} {1.0 1.0 0.0} '
|
|
294
|
+
|
|
295
|
+
Note that we had to index the arrow to get the Arrow graphics
|
|
296
|
+
primitive. We can also change the height via the :meth:`Graphics.plot3d`
|
|
297
|
+
method, but only as a whole::
|
|
298
|
+
|
|
299
|
+
sage: A = arrow((0,0),(1,1)).plot3d(3)
|
|
300
|
+
sage: A.jmol_repr(A.testing_render_params())[0][0]
|
|
301
|
+
'draw line_1 diameter 2 arrow {0.0 0.0 3.0} {1.0 1.0 3.0} '
|
|
302
|
+
|
|
303
|
+
Optional arguments place both the head and tail outside the
|
|
304
|
+
`xy`-plane, but at different heights. This must be done on
|
|
305
|
+
the graphics primitive obtained by indexing::
|
|
306
|
+
|
|
307
|
+
sage: A=arrow((0,0),(1,1))[0].plot3d(3,4)
|
|
308
|
+
sage: A.jmol_repr(A.testing_render_params())[0]
|
|
309
|
+
'draw line_1 diameter 2 arrow {0.0 0.0 3.0} {1.0 1.0 4.0} '
|
|
310
|
+
"""
|
|
311
|
+
from sage.plot.plot3d.shapes2 import line3d
|
|
312
|
+
options = self._plot3d_options()
|
|
313
|
+
options.update(kwds)
|
|
314
|
+
return line3d([(self.xtail, self.ytail, ztail), (self.xhead, self.yhead, zhead)], arrow_head=True, **options)
|
|
315
|
+
|
|
316
|
+
def _repr_(self):
|
|
317
|
+
"""
|
|
318
|
+
Text representation of an arrow graphics primitive.
|
|
319
|
+
|
|
320
|
+
EXAMPLES::
|
|
321
|
+
|
|
322
|
+
sage: from sage.plot.arrow import Arrow
|
|
323
|
+
sage: Arrow(0,0,2,3,{})._repr_()
|
|
324
|
+
'Arrow from (0.0,0.0) to (2.0,3.0)'
|
|
325
|
+
"""
|
|
326
|
+
return f"Arrow from ({self.xtail},{self.ytail}) to ({self.xhead},{self.yhead})"
|
|
327
|
+
|
|
328
|
+
def _render_on_subplot(self, subplot):
|
|
329
|
+
r"""
|
|
330
|
+
Render this arrow in a subplot. This is the key function that
|
|
331
|
+
defines how this arrow graphics primitive is rendered in
|
|
332
|
+
matplotlib's library.
|
|
333
|
+
|
|
334
|
+
EXAMPLES:
|
|
335
|
+
|
|
336
|
+
This function implicitly ends up rendering this arrow on
|
|
337
|
+
a matplotlib subplot::
|
|
338
|
+
|
|
339
|
+
sage: arrow((0,1), (2,-1))
|
|
340
|
+
Graphics object consisting of 1 graphics primitive
|
|
341
|
+
|
|
342
|
+
TESTS:
|
|
343
|
+
|
|
344
|
+
The length of the ends (shrinkA and shrinkB) should not depend
|
|
345
|
+
on the width of the arrow, because Matplotlib already takes
|
|
346
|
+
this into account. See :issue:`12836`::
|
|
347
|
+
|
|
348
|
+
sage: fig = Graphics().matplotlib()
|
|
349
|
+
sage: sp = fig.add_subplot(1,1,1, label='axis1')
|
|
350
|
+
sage: a = arrow((0,0), (1,1))
|
|
351
|
+
sage: b = arrow((0,0), (1,1), width=20)
|
|
352
|
+
sage: p1 = a[0]._render_on_subplot(sp)
|
|
353
|
+
sage: p2 = b[0]._render_on_subplot(sp)
|
|
354
|
+
sage: p1.shrinkA == p2.shrinkA
|
|
355
|
+
True
|
|
356
|
+
sage: p1.shrinkB == p2.shrinkB
|
|
357
|
+
True
|
|
358
|
+
|
|
359
|
+
Dashed arrows should have solid arrowheads, :issue:`12852`. We tried to
|
|
360
|
+
make up a test for this, which turned out to be fragile and hence was
|
|
361
|
+
removed. In general, robust testing of graphics seems basically need a
|
|
362
|
+
human eye or AI.
|
|
363
|
+
"""
|
|
364
|
+
from sage.plot.misc import get_matplotlib_linestyle
|
|
365
|
+
|
|
366
|
+
options = self.options()
|
|
367
|
+
head = options.pop('head')
|
|
368
|
+
if head == 0:
|
|
369
|
+
style = '<|-'
|
|
370
|
+
elif head == 1:
|
|
371
|
+
style = '-|>'
|
|
372
|
+
elif head == 2:
|
|
373
|
+
style = '<|-|>'
|
|
374
|
+
else:
|
|
375
|
+
raise KeyError('head parameter must be one of 0 (start), 1 (end) or 2 (both)')
|
|
376
|
+
width = float(options['width'])
|
|
377
|
+
arrowshorten_end = float(options.get('arrowshorten', 0)) / 2.0
|
|
378
|
+
arrowsize = float(options.get('arrowsize', 5))
|
|
379
|
+
head_width = arrowsize
|
|
380
|
+
head_length = arrowsize * 2.0
|
|
381
|
+
color = to_mpl_color(options['rgbcolor'])
|
|
382
|
+
from matplotlib.patches import FancyArrowPatch
|
|
383
|
+
p = FancyArrowPatch((self.xtail, self.ytail), (self.xhead, self.yhead),
|
|
384
|
+
lw=width,
|
|
385
|
+
arrowstyle='{},head_width={},head_length={}'.format(style, head_width, head_length),
|
|
386
|
+
shrinkA=arrowshorten_end, shrinkB=arrowshorten_end,
|
|
387
|
+
fc=color, ec=color,
|
|
388
|
+
linestyle=get_matplotlib_linestyle(options['linestyle'], return_type='long'))
|
|
389
|
+
p.set_zorder(options['zorder'])
|
|
390
|
+
p.set_label(options['legend_label'])
|
|
391
|
+
|
|
392
|
+
if options['linestyle'] != 'solid':
|
|
393
|
+
# The next few lines work around a design issue in matplotlib.
|
|
394
|
+
# Currently, the specified linestyle is used to draw both the path
|
|
395
|
+
# and the arrowhead. If linestyle is 'dashed', this looks really
|
|
396
|
+
# odd. This code is from Jae-Joon Lee in response to a post to the
|
|
397
|
+
# matplotlib mailing list.
|
|
398
|
+
# See http://sourceforge.net/mailarchive/forum.php?thread_name=CAG%3DuJ%2Bnw2dE05P9TOXTz_zp-mGP3cY801vMH7yt6vgP9_WzU8w%40mail.gmail.com&forum_name=matplotlib-users
|
|
399
|
+
|
|
400
|
+
import matplotlib.patheffects as pe
|
|
401
|
+
|
|
402
|
+
class CheckNthSubPath:
|
|
403
|
+
def __init__(self, patch, n):
|
|
404
|
+
"""
|
|
405
|
+
Creates a callable object that returns ``True`` if the
|
|
406
|
+
provided path is the n-th path from the patch.
|
|
407
|
+
"""
|
|
408
|
+
self._patch = patch
|
|
409
|
+
self._n = n
|
|
410
|
+
|
|
411
|
+
def get_paths(self, renderer):
|
|
412
|
+
# get_path_in_displaycoord was made private in matplotlib 3.5
|
|
413
|
+
try:
|
|
414
|
+
paths, fillables = self._patch._get_path_in_displaycoord()
|
|
415
|
+
except AttributeError:
|
|
416
|
+
paths, fillables = self._patch.get_path_in_displaycoord()
|
|
417
|
+
return paths
|
|
418
|
+
|
|
419
|
+
def __call__(self, renderer, gc, tpath, affine, rgbFace):
|
|
420
|
+
paths = self.get_paths(renderer)
|
|
421
|
+
if self._n >= len(paths):
|
|
422
|
+
return False
|
|
423
|
+
path = paths[self._n]
|
|
424
|
+
vert1, code1 = path.vertices, path.codes
|
|
425
|
+
import numpy as np
|
|
426
|
+
|
|
427
|
+
return np.array_equal(vert1, tpath.vertices) and np.array_equal(code1, tpath.codes)
|
|
428
|
+
|
|
429
|
+
class ConditionalStroke(pe.RendererBase):
|
|
430
|
+
|
|
431
|
+
def __init__(self, condition_func, pe_list):
|
|
432
|
+
"""
|
|
433
|
+
Path effect that is only applied when the ``condition_func``
|
|
434
|
+
returns ``True``.
|
|
435
|
+
"""
|
|
436
|
+
super().__init__()
|
|
437
|
+
self._pe_list = pe_list
|
|
438
|
+
self._condition_func = condition_func
|
|
439
|
+
|
|
440
|
+
def draw_path(self, renderer, gc, tpath, affine, rgbFace):
|
|
441
|
+
|
|
442
|
+
if self._condition_func(renderer, gc, tpath, affine, rgbFace):
|
|
443
|
+
for pe1 in self._pe_list:
|
|
444
|
+
pe1.draw_path(renderer, gc, tpath, affine, rgbFace)
|
|
445
|
+
|
|
446
|
+
pe1 = ConditionalStroke(CheckNthSubPath(p, 0), [pe.Stroke()])
|
|
447
|
+
pe2 = ConditionalStroke(CheckNthSubPath(p, 1), [pe.Stroke(dashes={'dash_offset': 0, 'dash_list': None})])
|
|
448
|
+
p.set_path_effects([pe1, pe2])
|
|
449
|
+
|
|
450
|
+
subplot.add_patch(p)
|
|
451
|
+
return p
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def arrow(tailpoint=None, headpoint=None, **kwds):
|
|
455
|
+
"""
|
|
456
|
+
Return either a 2-dimensional or 3-dimensional arrow depending
|
|
457
|
+
on value of points.
|
|
458
|
+
|
|
459
|
+
For information regarding additional arguments, see either arrow2d?
|
|
460
|
+
or arrow3d?.
|
|
461
|
+
|
|
462
|
+
EXAMPLES::
|
|
463
|
+
|
|
464
|
+
sage: arrow((0,0), (1,1))
|
|
465
|
+
Graphics object consisting of 1 graphics primitive
|
|
466
|
+
|
|
467
|
+
.. PLOT::
|
|
468
|
+
|
|
469
|
+
sphinx_plot(arrow((0,0), (1,1)))
|
|
470
|
+
|
|
471
|
+
::
|
|
472
|
+
|
|
473
|
+
sage: arrow((0,0,1), (1,1,1))
|
|
474
|
+
Graphics3d Object
|
|
475
|
+
|
|
476
|
+
.. PLOT::
|
|
477
|
+
|
|
478
|
+
sphinx_plot(arrow((0,0,1), (1,1,1)))
|
|
479
|
+
|
|
480
|
+
TESTS:
|
|
481
|
+
|
|
482
|
+
Check that :issue:`35031` is fixed::
|
|
483
|
+
|
|
484
|
+
sage: arrow((0,0), (0,0), linestyle='dashed')
|
|
485
|
+
Graphics object consisting of 1 graphics primitive
|
|
486
|
+
"""
|
|
487
|
+
try:
|
|
488
|
+
return arrow2d(tailpoint, headpoint, **kwds)
|
|
489
|
+
except ValueError:
|
|
490
|
+
from sage.plot.plot3d.shapes import arrow3d
|
|
491
|
+
return arrow3d(tailpoint, headpoint, **kwds)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@rename_keyword(color='rgbcolor')
|
|
495
|
+
@options(width=2, rgbcolor=(0,0,1), zorder=2, head=1, linestyle='solid',
|
|
496
|
+
legend_label=None, legend_color=None)
|
|
497
|
+
def arrow2d(tailpoint=None, headpoint=None, path=None, **options):
|
|
498
|
+
"""
|
|
499
|
+
If ``tailpoint`` and ``headpoint`` are provided, returns an arrow from
|
|
500
|
+
(xtail, ytail) to (xhead, yhead). If ``tailpoint`` or ``headpoint`` is None and
|
|
501
|
+
``path`` is not None, returns an arrow along the path. (See further info on
|
|
502
|
+
paths in :class:`bezier_path`).
|
|
503
|
+
|
|
504
|
+
INPUT:
|
|
505
|
+
|
|
506
|
+
- ``tailpoint`` -- the starting point of the arrow
|
|
507
|
+
|
|
508
|
+
- ``headpoint`` -- where the arrow is pointing to
|
|
509
|
+
|
|
510
|
+
- ``path`` -- the list of points and control points (see bezier_path for
|
|
511
|
+
detail) that the arrow will follow from source to destination
|
|
512
|
+
|
|
513
|
+
- ``head`` -- 0, 1 or 2, whether to draw the head at the start (0), end (1)
|
|
514
|
+
or both (2) of the path (using 0 will swap headpoint and tailpoint).
|
|
515
|
+
This is ignored in 3D plotting.
|
|
516
|
+
|
|
517
|
+
- ``linestyle`` -- (default: ``'solid'``) the style of the line, which is
|
|
518
|
+
one of ``'dashed'``, ``'dotted'``, ``'solid'``, ``'dashdot'``,
|
|
519
|
+
or ``'--'``, ``':'``, ``'-'``, ``'-.'``, respectively
|
|
520
|
+
|
|
521
|
+
- ``width`` -- (default: 2) the width of the arrow shaft, in points
|
|
522
|
+
|
|
523
|
+
- ``color`` -- (default: (0,0,1)) the color of the arrow (as an RGB tuple or
|
|
524
|
+
a string)
|
|
525
|
+
|
|
526
|
+
- ``hue`` -- the color of the arrow (as a number)
|
|
527
|
+
|
|
528
|
+
- ``arrowsize`` -- the size of the arrowhead
|
|
529
|
+
|
|
530
|
+
- ``arrowshorten`` -- the length in points to shorten the arrow (ignored if
|
|
531
|
+
using path parameter)
|
|
532
|
+
|
|
533
|
+
- ``legend_label`` -- the label for this item in the legend
|
|
534
|
+
|
|
535
|
+
- ``legend_color`` -- the color for the legend label
|
|
536
|
+
|
|
537
|
+
- ``zorder`` -- the layer level to draw the arrow-- note that this is
|
|
538
|
+
ignored in 3D plotting
|
|
539
|
+
|
|
540
|
+
EXAMPLES:
|
|
541
|
+
|
|
542
|
+
A straight, blue arrow::
|
|
543
|
+
|
|
544
|
+
sage: arrow2d((1,1), (3,3))
|
|
545
|
+
Graphics object consisting of 1 graphics primitive
|
|
546
|
+
|
|
547
|
+
.. PLOT::
|
|
548
|
+
|
|
549
|
+
sphinx_plot(arrow2d((1,1), (3,3)))
|
|
550
|
+
|
|
551
|
+
Make a red arrow::
|
|
552
|
+
|
|
553
|
+
sage: arrow2d((-1,-1), (2,3), color=(1,0,0))
|
|
554
|
+
Graphics object consisting of 1 graphics primitive
|
|
555
|
+
|
|
556
|
+
.. PLOT::
|
|
557
|
+
|
|
558
|
+
sphinx_plot(arrow2d((-1,-1), (2,3), color=(1,0,0)))
|
|
559
|
+
|
|
560
|
+
::
|
|
561
|
+
|
|
562
|
+
sage: arrow2d((-1,-1), (2,3), color='red')
|
|
563
|
+
Graphics object consisting of 1 graphics primitive
|
|
564
|
+
|
|
565
|
+
.. PLOT::
|
|
566
|
+
|
|
567
|
+
sphinx_plot(arrow2d((-1,-1), (2,3), color='red'))
|
|
568
|
+
|
|
569
|
+
You can change the width of an arrow::
|
|
570
|
+
|
|
571
|
+
sage: arrow2d((1,1), (3,3), width=5, arrowsize=15)
|
|
572
|
+
Graphics object consisting of 1 graphics primitive
|
|
573
|
+
|
|
574
|
+
.. PLOT::
|
|
575
|
+
|
|
576
|
+
P = arrow2d((1,1), (3,3), width=5, arrowsize=15)
|
|
577
|
+
sphinx_plot(P)
|
|
578
|
+
|
|
579
|
+
Use a dashed line instead of a solid one for the arrow::
|
|
580
|
+
|
|
581
|
+
sage: arrow2d((1,1), (3,3), linestyle='dashed')
|
|
582
|
+
Graphics object consisting of 1 graphics primitive
|
|
583
|
+
sage: arrow2d((1,1), (3,3), linestyle='--')
|
|
584
|
+
Graphics object consisting of 1 graphics primitive
|
|
585
|
+
|
|
586
|
+
.. PLOT::
|
|
587
|
+
|
|
588
|
+
P = arrow2d((1,1), (3,3), linestyle='--')
|
|
589
|
+
sphinx_plot(P)
|
|
590
|
+
|
|
591
|
+
A pretty circle of arrows::
|
|
592
|
+
|
|
593
|
+
sage: sum(arrow2d((0,0), (cos(x),sin(x)), hue=x/(2*pi)) # needs sage.symbolic
|
|
594
|
+
....: for x in [0..2*pi, step=0.1])
|
|
595
|
+
Graphics object consisting of 63 graphics primitives
|
|
596
|
+
|
|
597
|
+
.. PLOT::
|
|
598
|
+
|
|
599
|
+
P = sum([arrow2d((0,0), (cos(x*0.1),sin(x*0.1)), hue=x/(20*pi)) for x in range(floor(20*pi)+1)])
|
|
600
|
+
sphinx_plot(P)
|
|
601
|
+
|
|
602
|
+
If we want to draw the arrow between objects, for example, the
|
|
603
|
+
boundaries of two lines, we can use the ``arrowshorten`` option
|
|
604
|
+
to make the arrow shorter by a certain number of points::
|
|
605
|
+
|
|
606
|
+
sage: L1 = line([(0,0), (1,0)], thickness=10)
|
|
607
|
+
sage: L2 = line([(0,1), (1,1)], thickness=10)
|
|
608
|
+
sage: A = arrow2d((0.5,0), (0.5,1), arrowshorten=10, rgbcolor=(1,0,0))
|
|
609
|
+
sage: L1 + L2 + A
|
|
610
|
+
Graphics object consisting of 3 graphics primitives
|
|
611
|
+
|
|
612
|
+
.. PLOT::
|
|
613
|
+
|
|
614
|
+
L1 = line([(0,0), (1,0)],thickness=10)
|
|
615
|
+
L2 = line([(0,1), (1,1)], thickness=10)
|
|
616
|
+
A = arrow2d((0.5,0), (0.5,1), arrowshorten=10, rgbcolor=(1,0,0))
|
|
617
|
+
sphinx_plot(L1 + L2 + A)
|
|
618
|
+
|
|
619
|
+
If BOTH ``headpoint`` and ``tailpoint`` are None, then an empty plot is
|
|
620
|
+
returned::
|
|
621
|
+
|
|
622
|
+
sage: arrow2d(headpoint=None, tailpoint=None)
|
|
623
|
+
Graphics object consisting of 0 graphics primitives
|
|
624
|
+
|
|
625
|
+
We can also draw an arrow with a legend::
|
|
626
|
+
|
|
627
|
+
sage: arrow((0,0), (0,2), legend_label='up', legend_color='purple')
|
|
628
|
+
Graphics object consisting of 1 graphics primitive
|
|
629
|
+
|
|
630
|
+
.. PLOT::
|
|
631
|
+
|
|
632
|
+
P = arrow((0,0), (0,2), legend_label='up', legend_color='purple')
|
|
633
|
+
sphinx_plot(P)
|
|
634
|
+
|
|
635
|
+
Extra options will get passed on to :meth:`Graphics.show()`, as long as they are valid::
|
|
636
|
+
|
|
637
|
+
sage: arrow2d((-2,2), (7,1), frame=True)
|
|
638
|
+
Graphics object consisting of 1 graphics primitive
|
|
639
|
+
|
|
640
|
+
.. PLOT::
|
|
641
|
+
|
|
642
|
+
sphinx_plot(arrow2d((-2,2), (7,1), frame=True))
|
|
643
|
+
|
|
644
|
+
::
|
|
645
|
+
|
|
646
|
+
sage: arrow2d((-2,2), (7,1)).show(frame=True)
|
|
647
|
+
|
|
648
|
+
TESTS:
|
|
649
|
+
|
|
650
|
+
Verify that :issue:`36153` is fixed::
|
|
651
|
+
|
|
652
|
+
sage: A = arrow2d((-1,-1), (2,3), legend_label='test')
|
|
653
|
+
"""
|
|
654
|
+
from sage.plot.all import Graphics
|
|
655
|
+
g = Graphics()
|
|
656
|
+
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
|
|
657
|
+
|
|
658
|
+
if headpoint is not None and tailpoint is not None:
|
|
659
|
+
xtail, ytail = tailpoint
|
|
660
|
+
xhead, yhead = headpoint
|
|
661
|
+
g.add_primitive(Arrow(xtail, ytail, xhead, yhead, options=options))
|
|
662
|
+
elif path is not None:
|
|
663
|
+
g.add_primitive(CurveArrow(path, options=options))
|
|
664
|
+
elif tailpoint is None and headpoint is None:
|
|
665
|
+
return g
|
|
666
|
+
else:
|
|
667
|
+
raise TypeError('arrow requires either both headpoint and tailpoint or a path parameter')
|
|
668
|
+
if options['legend_label']:
|
|
669
|
+
g.legend(True)
|
|
670
|
+
g._legend_colors = [options['legend_color']]
|
|
671
|
+
return g
|