Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/plot/line.py
8815 views
1
"""
2
Line Plots
3
"""
4
5
#*****************************************************************************
6
# Copyright (C) 2006 Alex Clemesha <[email protected]>,
7
# William Stein <[email protected]>,
8
# 2008 Mike Hansen <[email protected]>,
9
#
10
# Distributed under the terms of the GNU General Public License (GPL)
11
#
12
# This code is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
# General Public License for more details.
16
#
17
# The full text of the GPL is available at:
18
#
19
# http://www.gnu.org/licenses/
20
#*****************************************************************************
21
from sage.plot.primitive import GraphicPrimitive_xydata
22
from sage.misc.decorators import options, rename_keyword
23
from sage.plot.colors import to_mpl_color
24
25
class Line(GraphicPrimitive_xydata):
26
"""
27
Primitive class that initializes the line graphics type.
28
29
EXAMPLES::
30
31
sage: from sage.plot.line import Line
32
sage: Line([1,2,7], [1,5,-1], {})
33
Line defined by 3 points
34
"""
35
def __init__(self, xdata, ydata, options):
36
"""
37
Initialize a line graphics primitive.
38
39
EXAMPLES::
40
41
sage: from sage.plot.line import Line
42
sage: Line([-1,2], [17,4], {'thickness':2})
43
Line defined by 2 points
44
"""
45
valid_options = self._allowed_options().keys()
46
for opt in options.iterkeys():
47
if opt not in valid_options:
48
raise RuntimeError("Error in line(): option '%s' not valid."%opt)
49
self.xdata = xdata
50
self.ydata = ydata
51
GraphicPrimitive_xydata.__init__(self, options)
52
53
def _allowed_options(self):
54
"""
55
Displayed the list of allowed line options.
56
57
EXAMPLES::
58
59
sage: from sage.plot.line import Line
60
sage: list(sorted(Line([-1,2], [17,4], {})._allowed_options().iteritems()))
61
[('alpha', 'How transparent the line is.'),
62
('hue', 'The color given as a hue.'),
63
('legend_color', 'The color of the legend text.'),
64
('legend_label', 'The label for this item in the legend.'),
65
('linestyle',
66
"The style of the line, which is one of '--' (dashed), '-.' (dash dot), '-' (solid), 'steps', ':' (dotted)."),
67
('marker', 'the marker symbol (see documentation for line2d for details)'),
68
('markeredgecolor', 'the color of the marker edge'),
69
('markeredgewidth', 'the size of the marker edge in points'),
70
('markerfacecolor', 'the color of the marker face'),
71
('markersize', 'the size of the marker in points'),
72
('rgbcolor', 'The color as an RGB tuple.'),
73
('thickness', 'How thick the line is.'),
74
('zorder', 'The layer level in which to draw')]
75
"""
76
return {'alpha':'How transparent the line is.',
77
'legend_color':'The color of the legend text.',
78
'legend_label':'The label for this item in the legend.',
79
'thickness':'How thick the line is.',
80
'rgbcolor':'The color as an RGB tuple.',
81
'hue':'The color given as a hue.',
82
'linestyle':"The style of the line, which is one of '--' (dashed), '-.' (dash dot), '-' (solid), 'steps', ':' (dotted).",
83
'marker':"the marker symbol (see documentation for line2d for details)",
84
'markersize':'the size of the marker in points',
85
'markeredgecolor':'the color of the marker edge',
86
'markeredgewidth':'the size of the marker edge in points',
87
'markerfacecolor':'the color of the marker face',
88
'zorder':'The layer level in which to draw'
89
}
90
91
def _plot3d_options(self, options=None):
92
"""
93
Translate 2D plot options into 3D plot options.
94
95
EXAMPLES::
96
97
sage: L = line([(1,1), (1,2), (2,2), (2,1)], alpha=.5, thickness=10, zorder=2)
98
sage: l=L[0]; l
99
Line defined by 4 points
100
sage: m=l.plot3d(z=2)
101
sage: m.texture.opacity
102
0.500000000000000
103
sage: m.thickness
104
10
105
sage: L = line([(1,1), (1,2), (2,2), (2,1)], linestyle=":")
106
sage: L.plot3d()
107
Traceback (most recent call last):
108
NotImplementedError: Invalid 3d line style: ':'
109
"""
110
if options == None:
111
options = dict(self.options())
112
options_3d = {}
113
if 'thickness' in options:
114
options_3d['thickness'] = options['thickness']
115
del options['thickness']
116
if 'zorder' in options:
117
del options['zorder']
118
if 'linestyle' in options:
119
if options['linestyle'] not in ('-', 'solid'):
120
raise NotImplementedError("Invalid 3d line style: '%s'"%
121
(options['linestyle']))
122
del options['linestyle']
123
options_3d.update(GraphicPrimitive_xydata._plot3d_options(self, options))
124
return options_3d
125
126
def plot3d(self, z=0, **kwds):
127
"""
128
Plots a 2D line in 3D, with default height zero.
129
130
EXAMPLES::
131
132
sage: E = EllipticCurve('37a').plot(thickness=5).plot3d()
133
sage: F = EllipticCurve('37a').plot(thickness=5).plot3d(z=2)
134
sage: E + F # long time (5s on sage.math, 2012)
135
"""
136
from sage.plot.plot3d.shapes2 import line3d
137
options = self._plot3d_options()
138
options.update(kwds)
139
return line3d([(x, y, z) for x, y in zip(self.xdata, self.ydata)], **options)
140
141
def _repr_(self):
142
"""
143
String representation of a line primitive.
144
145
EXAMPLES::
146
147
sage: from sage.plot.line import Line
148
sage: Line([-1,2,3,3], [17,4,0,2], {})._repr_()
149
'Line defined by 4 points'
150
"""
151
return "Line defined by %s points"%len(self)
152
153
def __getitem__(self, i):
154
"""
155
Extract the i-th element of the line (which is stored as a list of points).
156
157
INPUT:
158
159
- ``i`` -- an integer between 0 and the number of points minus 1
160
161
OUTPUT:
162
163
A 2-tuple of floats.
164
165
EXAMPLES::
166
167
sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
168
sage: line_primitive = L[0]; line_primitive
169
Line defined by 4 points
170
sage: line_primitive[0]
171
(1.0, 2.0)
172
sage: line_primitive[2]
173
(2.0, 5.0)
174
sage: list(line_primitive)
175
[(1.0, 2.0), (3.0, -4.0), (2.0, 5.0), (1.0, 2.0)]
176
"""
177
return self.xdata[i], self.ydata[i]
178
179
def __setitem__(self, i, point):
180
"""
181
Set the i-th element of this line (really a sequence of lines
182
through given points).
183
184
INPUT:
185
186
- ``i`` -- an integer between 0 and the number of points on the
187
line minus 1
188
189
- ``point`` -- a 2-tuple of floats
190
191
EXAMPLES:
192
193
We create a line graphics object $L$ and get ahold of the
194
corresponding line graphics primitive::
195
196
sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
197
sage: line_primitive = L[0]; line_primitive
198
Line defined by 4 points
199
200
We then set the 0th point to `(0,0)` instead of `(1,2)`::
201
202
sage: line_primitive[0] = (0,0)
203
sage: line_primitive[0]
204
(0.0, 0.0)
205
206
Plotting we visibly see the change -- now the line starts at `(0,0)`::
207
208
sage: L
209
"""
210
self.xdata[i] = float(point[0])
211
self.ydata[i] = float(point[1])
212
213
def __len__(self):
214
r"""
215
Return the number of points on this line (where a line is really a sequence
216
of line segments through a given list of points).
217
218
EXAMPLES:
219
220
We create a line, then grab the line primitive as ``L[0]`` and compute
221
its length::
222
223
sage: L = line([(1,2), (3,-4), (2, 5), (1,2)])
224
sage: len(L[0])
225
4
226
"""
227
return len(self.xdata)
228
229
def _render_on_subplot(self, subplot):
230
"""
231
Render this line on a matplotlib subplot.
232
233
INPUT:
234
235
- ``subplot`` -- a matplotlib subplot
236
237
EXAMPLES:
238
239
This implicitly calls this function::
240
241
sage: line([(1,2), (3,-4), (2, 5), (1,2)])
242
"""
243
import matplotlib.lines as lines
244
options = dict(self.options())
245
for o in ('alpha', 'legend_color', 'legend_label', 'linestyle',
246
'rgbcolor', 'thickness'):
247
if o in options:
248
del options[o]
249
p = lines.Line2D(self.xdata, self.ydata, **options)
250
options = self.options()
251
a = float(options['alpha'])
252
p.set_alpha(a)
253
p.set_linewidth(float(options['thickness']))
254
p.set_color(to_mpl_color(options['rgbcolor']))
255
p.set_label(options['legend_label'])
256
# we don't pass linestyle in directly since the drawstyles aren't
257
# pulled off automatically. This (I think) is a bug in matplotlib 1.0.1
258
if 'linestyle' in options:
259
from sage.plot.misc import get_matplotlib_linestyle
260
p.set_linestyle(get_matplotlib_linestyle(options['linestyle'],
261
return_type='short'))
262
subplot.add_line(p)
263
264
def line(points, **kwds):
265
"""
266
Returns either a 2-dimensional or 3-dimensional line depending
267
on value of points.
268
269
INPUT:
270
271
- ``points`` - either a single point (as a tuple), a list of
272
points, a single complex number, or a list of complex numbers.
273
274
For information regarding additional arguments, see either line2d?
275
or line3d?.
276
277
EXAMPLES::
278
279
sage: line([(0,0), (1,1)])
280
281
::
282
283
sage: line([(0,0,1), (1,1,1)])
284
"""
285
try:
286
return line2d(points, **kwds)
287
except ValueError:
288
from sage.plot.plot3d.shapes2 import line3d
289
return line3d(points, **kwds)
290
291
292
@rename_keyword(color='rgbcolor')
293
@options(alpha=1, rgbcolor=(0,0,1), thickness=1, legend_label=None,
294
legend_color=None, aspect_ratio ='automatic')
295
def line2d(points, **options):
296
r"""
297
Create the line through the given list of points.
298
299
INPUT:
300
301
- ``points`` - either a single point (as a tuple), a list of
302
points, a single complex number, or a list of complex numbers.
303
304
Type ``line2d.options`` for a dictionary of the default options for
305
lines. You can change this to change the defaults for all future
306
lines. Use ``line2d.reset()`` to reset to the default options.
307
308
INPUT:
309
310
- ``alpha`` -- How transparent the line is
311
312
- ``thickness`` -- How thick the line is
313
314
- ``rgbcolor`` -- The color as an RGB tuple
315
316
- ``hue`` -- The color given as a hue
317
318
- ``legend_color`` -- The color of the text in the legend
319
320
- ``legend_label`` -- the label for this item in the legend
321
322
323
Any MATPLOTLIB line option may also be passed in. E.g.,
324
325
- ``linestyle`` - (default: "-") The style of the line, which is one of
326
- ``"-"`` or ``"solid"``
327
- ``"--"`` or ``"dashed"``
328
- ``"-."`` or ``"dash dot"``
329
- ``":"`` or ``"dotted"``
330
- ``"None"`` or ``" "`` or ``""`` (nothing)
331
332
The linestyle can also be prefixed with a drawing style (e.g., ``"steps--"``)
333
334
- ``"default"`` (connect the points with straight lines)
335
- ``"steps"`` or ``"steps-pre"`` (step function; horizontal
336
line is to the left of point)
337
- ``"steps-mid"`` (step function; points are in the middle of
338
horizontal lines)
339
- ``"steps-post"`` (step function; horizontal line is to the
340
right of point)
341
342
- ``marker`` - The style of the markers, which is one of
343
- ``"None"`` or ``" "`` or ``""`` (nothing) -- default
344
- ``","`` (pixel), ``"."`` (point)
345
- ``"_"`` (horizontal line), ``"|"`` (vertical line)
346
- ``"o"`` (circle), ``"p"`` (pentagon), ``"s"`` (square), ``"x"`` (x), ``"+"`` (plus), ``"*"`` (star)
347
- ``"D"`` (diamond), ``"d"`` (thin diamond)
348
- ``"H"`` (hexagon), ``"h"`` (alternative hexagon)
349
- ``"<"`` (triangle left), ``">"`` (triangle right), ``"^"`` (triangle up), ``"v"`` (triangle down)
350
- ``"1"`` (tri down), ``"2"`` (tri up), ``"3"`` (tri left), ``"4"`` (tri right)
351
- ``0`` (tick left), ``1`` (tick right), ``2`` (tick up), ``3`` (tick down)
352
- ``4`` (caret left), ``5`` (caret right), ``6`` (caret up), ``7`` (caret down)
353
- ``"$...$"`` (math TeX string)
354
355
- ``markersize`` -- the size of the marker in points
356
357
- ``markeredgecolor`` -- the color of the marker edge
358
359
- ``markerfacecolor`` -- the color of the marker face
360
361
- ``markeredgewidth`` -- the size of the marker edge in points
362
363
EXAMPLES:
364
365
A line with no points or one point::
366
367
sage: line([]) #returns an empty plot
368
sage: import numpy; line(numpy.array([]))
369
sage: line([(1,1)])
370
371
A line with numpy arrays::
372
373
sage: line(numpy.array([[1,2], [3,4]]))
374
375
A line with a legend::
376
377
sage: line([(0,0),(1,1)], legend_label='line')
378
379
Lines with different colors in the legend text::
380
381
sage: p1 = line([(0,0),(1,1)], legend_label='line')
382
sage: p2 = line([(1,1),(2,4)], legend_label='squared', legend_color='red')
383
sage: p1 + p2
384
385
Extra options will get passed on to show(), as long as they are valid::
386
387
sage: line([(0,1), (3,4)], figsize=[10, 2])
388
sage: line([(0,1), (3,4)]).show(figsize=[10, 2]) # These are equivalent
389
390
We can also use a logarithmic scale if the data will support it::
391
392
sage: line([(1,2),(2,4),(3,4),(4,8),(4.5,32)],scale='loglog',base=2)
393
394
Many more examples below!
395
396
A blue conchoid of Nicomedes::
397
398
sage: L = [[1+5*cos(pi/2+pi*i/100), tan(pi/2+pi*i/100)*(1+5*cos(pi/2+pi*i/100))] for i in range(1,100)]
399
sage: line(L, rgbcolor=(1/4,1/8,3/4))
400
401
A line with 2 complex points::
402
403
sage: i = CC.0
404
sage: line([1+i, 2+3*i])
405
406
A blue hypotrochoid (3 leaves)::
407
408
sage: n = 4; h = 3; b = 2
409
sage: L = [[n*cos(pi*i/100)+h*cos((n/b)*pi*i/100),n*sin(pi*i/100)-h*sin((n/b)*pi*i/100)] for i in range(200)]
410
sage: line(L, rgbcolor=(1/4,1/4,3/4))
411
412
A blue hypotrochoid (4 leaves)::
413
414
sage: n = 6; h = 5; b = 2
415
sage: L = [[n*cos(pi*i/100)+h*cos((n/b)*pi*i/100),n*sin(pi*i/100)-h*sin((n/b)*pi*i/100)] for i in range(200)]
416
sage: line(L, rgbcolor=(1/4,1/4,3/4))
417
418
A red limacon of Pascal::
419
420
sage: L = [[sin(pi*i/100)+sin(pi*i/50),-(1+cos(pi*i/100)+cos(pi*i/50))] for i in range(-100,101)]
421
sage: line(L, rgbcolor=(1,1/4,1/2))
422
423
A light green trisectrix of Maclaurin::
424
425
sage: L = [[2*(1-4*cos(-pi/2+pi*i/100)^2),10*tan(-pi/2+pi*i/100)*(1-4*cos(-pi/2+pi*i/100)^2)] for i in range(1,100)]
426
sage: line(L, rgbcolor=(1/4,1,1/8))
427
428
A green lemniscate of Bernoulli::
429
430
sage: cosines = [cos(-pi/2+pi*i/100) for i in range(201)]
431
sage: v = [(1/c, tan(-pi/2+pi*i/100)) for i,c in enumerate(cosines) if c != 0]
432
sage: L = [(a/(a^2+b^2), b/(a^2+b^2)) for a,b in v]
433
sage: line(L, rgbcolor=(1/4,3/4,1/8))
434
435
A red plot of the Jacobi elliptic function `\text{sn}(x,2)`, `-3 < x < 3`::
436
437
sage: L = [(i/100.0, jacobi('sn', i/100.0 ,2.0)) for i in range(-300,300,30)]
438
sage: line(L, rgbcolor=(3/4,1/4,1/8))
439
440
A red plot of `J`-Bessel function `J_2(x)`, `0 < x < 10`::
441
442
sage: L = [(i/10.0, bessel_J(2,i/10.0)) for i in range(100)]
443
sage: line(L, rgbcolor=(3/4,1/4,5/8))
444
445
446
A purple plot of the Riemann zeta function `\zeta(1/2 + it)`, `0 < t < 30`::
447
448
sage: i = CDF.gen()
449
sage: v = [zeta(0.5 + n/10 * i) for n in range(300)]
450
sage: L = [(z.real(), z.imag()) for z in v]
451
sage: line(L, rgbcolor=(3/4,1/2,5/8))
452
453
A purple plot of the Hasse-Weil `L`-function `L(E, 1 + it)`, `-1 < t < 10`::
454
455
sage: E = EllipticCurve('37a')
456
sage: vals = E.lseries().values_along_line(1-I, 1+10*I, 100) # critical line
457
sage: L = [(z[1].real(), z[1].imag()) for z in vals]
458
sage: line(L, rgbcolor=(3/4,1/2,5/8))
459
460
A red, blue, and green "cool cat"::
461
462
sage: G = plot(-cos(x), -2, 2, thickness=5, rgbcolor=(0.5,1,0.5))
463
sage: P = polygon([[1,2], [5,6], [5,0]], rgbcolor=(1,0,0))
464
sage: Q = polygon([(-x,y) for x,y in P[0]], rgbcolor=(0,0,1))
465
sage: G + P + Q # show the plot
466
467
TESTS:
468
469
Check that :trac:`13690` is fixed. The legend label should have circles
470
as markers.::
471
472
sage: line(enumerate(range(2)), marker='o', legend_label='circle')
473
474
"""
475
from sage.plot.all import Graphics
476
from sage.plot.plot import xydata_from_point_list
477
from sage.rings.all import CC, CDF
478
if points in CC or points in CDF:
479
pass
480
else:
481
try:
482
if not points:
483
return Graphics()
484
except ValueError: # numpy raises a ValueError if not empty
485
pass
486
xdata, ydata = xydata_from_point_list(points)
487
g = Graphics()
488
g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
489
g.add_primitive(Line(xdata, ydata, options))
490
if options['legend_label']:
491
g.legend(True)
492
g._legend_colors = [options['legend_color']]
493
return g
494
495