Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/hyperplane_arrangement/plot.py
8817 views
1
"""
2
Plotting of Hyperplane Arrangements
3
4
PLOT OPTIONS::
5
6
Beside the usual plot options (enter ``plot?``), the plot command for
7
hyperplane arrangements includes the following:
8
9
- ``hyperplane_colors`` -- Color or list of colors, one for each
10
hyperplane (default: equally spread range of hues).
11
12
- ``hyperplane_labels`` -- Boolean, ``'short'``, ``'long'`` (default:
13
``False``). If False, no labels are shown; if 'short' or 'long',
14
the hyperplanes are given short or long labels, respectively. If
15
``True``, the hyperplanes are given long labels.
16
17
- ``label_colors`` -- Color or list of colors, one for each hyperplane
18
(default: black).
19
20
- ``label_fontsize`` -- Size for hyperplane_label font (default:
21
``14``). This does not work for 3d plots.
22
23
- ``label_offsets`` -- Amount be which labels are offset from
24
h.point() for each hyperplane h. The format is different for each
25
dimension: if the hyperplanes have dimension 0, the offset can be a
26
single number or a list of numbers, one for each hyperplane; if the
27
hyperplanes have dimension 1, the offset can be a single 2-tuple, or
28
a list of 2-tuples, one for each hyperplane; if the hyperplanes have
29
dimension 2, the offset can be a single 3-tuple or a list of
30
3-tuples, one for each hyperplane. (Defaults: 0-dim: ``0.1``,
31
1-dim: ``(0,1)``, 2-dim: ``(0,0,0.2)``).
32
33
- ``hyperplane_legend`` -- Boolean, ``'short'``, ``'long'`` (default:
34
``'long'``; in 3-d: ``False``). If ``False``, no legend is shown;
35
if ``True``, ``'short'``, or ``'long'``, the legend is shown with
36
the default, long, or short labeling, respectively. (For
37
arrangements of lines or planes, only.)
38
39
- ``hyperplane_opacities`` -- A number or list of numbers, one for each
40
hyperplane, between 0 and 1. Only applies to 3d plots.
41
42
- ``point_sizes`` -- Number or list of numbers, one for each hyperplane
43
giving the sizes of points in a zero-dimensional arrangement
44
(default: ``50``).
45
46
- ``ranges`` -- Range for the parameters or a list of ranges of
47
parameters, one for each hyperplane, for the parametric plots of the
48
hyperplanes. If a single positive number `r` is given for
49
``ranges``, then all parameters run from -r to r. Otherwise, for a
50
line in the plane, the range has the form ``[a,b]`` (default:
51
``[-3,3]``), and for a plane in 3-space, the range has the form
52
``[[a,b],[c,d]]`` (default: ``[[-3,3],[-3,3]]``). The ranges are
53
centered around ``hyperplane_arrangement.point()``.
54
55
EXAMPLES::
56
57
sage: H3.<x,y,z> = HyperplaneArrangements(QQ)
58
sage: A = H3([(1,0,0), 0], [(0,0,1), 5])
59
sage: A.plot(hyperplane_opacities=0.5, hyperplane_labels=True, hyperplane_legend=False)
60
61
sage: c = H3([(1,0,0),0], [(0,0,1),5])
62
sage: c.plot(ranges=10)
63
sage: c.plot(ranges=[[9.5,10], [-3,3]])
64
sage: c.plot(ranges=[[[9.5,10], [-3,3]], [[-6,6], [-5,5]]])
65
66
67
sage: H2.<s,t> = HyperplaneArrangements(QQ)
68
sage: h = H2([(1,1),0], [(1,-1),0], [(0,1),2])
69
sage: h.plot(ranges=20)
70
sage: h.plot(ranges=[-1, 10])
71
sage: h.plot(ranges=[[-1, 1], [-5, 5], [-1, 10]])
72
73
sage: a = hyperplane_arrangements.coordinate(3)
74
sage: opts = {'hyperplane_colors':['yellow', 'green', 'blue']}
75
sage: opts['hyperplane_labels'] = True
76
sage: opts['label_offsets'] = [(0,2,2), (2,0,2), (2,2,0)]
77
sage: opts['hyperplane_legend'] = False
78
sage: opts['hyperplane_opacities'] = 0.7
79
sage: a.plot(**opts)
80
sage: opts['hyperplane_labels'] = 'short'
81
sage: a.plot(**opts)
82
83
sage: H.<u> = HyperplaneArrangements(QQ)
84
sage: pts = H(3*u+4, 2*u+5, 7*u+1)
85
sage: pts.plot(hyperplane_colors=['yellow','black','blue'])
86
sage: pts.plot(point_sizes=[50,100,200], hyperplane_colors='blue')
87
88
sage: H.<x,y,z> = HyperplaneArrangements(QQ)
89
sage: a = H(x, y+1, y+2)
90
sage: a.plot(hyperplane_labels=True,label_colors='blue',label_fontsize=18)
91
sage: a.plot(hyperplane_labels=True,label_colors=['red','green','black'])
92
"""
93
94
from copy import copy
95
from colorsys import hsv_to_rgb
96
from sage.plot.plot3d.parametric_plot3d import parametric_plot3d
97
from sage.plot.plot3d.shapes2 import text3d
98
from sage.plot.graphics import Graphics
99
from sage.plot.line import line
100
from sage.plot.text import text
101
from sage.plot.point import point
102
from sage.plot.plot import parametric_plot
103
from sage.symbolic.all import SR
104
105
106
def plot(hyperplane_arrangement, **kwds):
107
r"""
108
Return a plot of the hyperplane arrangement.
109
110
If the arrangement is in 4 dimensions but inessential, a plot of
111
the essentialization is returned.
112
113
.. NOTE::
114
115
This function is available as the
116
:meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.plot`
117
method of hyperplane arrangements. You should not call this
118
function directly, only through the method.
119
120
INPUT:
121
122
- ``hyperplane_arrangement`` -- the hyperplane arrangement to plot
123
124
- ``**kwds`` -- plot options: see
125
:mod:`sage.geometry.hyperplane_arrangement.plot`.
126
127
OUTPUT:
128
129
A graphics object of the plot.
130
131
EXAMPLES::
132
133
sage: B = hyperplane_arrangements.semiorder(4)
134
sage: B.plot()
135
Displaying the essentialization.
136
"""
137
N = len(hyperplane_arrangement)
138
dim = hyperplane_arrangement.dimension()
139
if hyperplane_arrangement.base_ring().characteristic() != 0:
140
raise NotImplementedError('must be a field of characteristic 0')
141
elif dim == 4:
142
if not hyperplane_arrangement.is_essential():
143
print 'Displaying the essentialization.'
144
hyperplane_arrangement = hyperplane_arrangement.essentialization()
145
elif dim not in [1,2,3]: # revise to handle 4d
146
return # silently
147
# handle extra keywords
148
if kwds.has_key('hyperplane_colors'):
149
hyp_colors = kwds.pop('hyperplane_colors')
150
if not type(hyp_colors) == list: # we assume its a single color then
151
hyp_colors = [hyp_colors] * N
152
else:
153
HSV_tuples = [(i*1.0/N, 0.8, 0.9) for i in range(N)]
154
hyp_colors = map(lambda x: hsv_to_rgb(*x), HSV_tuples)
155
if kwds.has_key('hyperplane_labels'):
156
hyp_labels = kwds.pop('hyperplane_labels')
157
has_hyp_label = True
158
if not type(hyp_labels) == list: # we assume its a boolean then
159
hyp_labels = [hyp_labels] * N
160
relabeled = []
161
for i in range(N):
162
if hyp_labels[i] in [True,'long']:
163
relabeled.append(True)
164
else:
165
relabeled.append(str(i))
166
hyp_labels = relabeled
167
else:
168
has_hyp_label = False
169
if kwds.has_key('label_colors'):
170
label_colors = kwds.pop('label_colors')
171
has_label_color = True
172
if not type(label_colors) == list: # we assume its a single color then
173
label_colors = [label_colors] * N
174
else:
175
has_label_color = False
176
if kwds.has_key('label_fontsize'):
177
label_fontsize = kwds.pop('label_fontsize')
178
has_label_fontsize = True
179
if not type(label_fontsize) == list: # we assume its a single size then
180
label_fontsize = [label_fontsize] * N
181
else:
182
has_label_fontsize = False
183
if kwds.has_key('label_offsets'):
184
has_offsets = True
185
offsets = kwds.pop('label_offsets')
186
else:
187
has_offsets = False # give default values below
188
hyperplane_legend = kwds.pop('hyperplane_legend', 'long' if dim < 3 else False)
189
if kwds.has_key('hyperplane_opacities'):
190
hyperplane_opacities = kwds.pop('hyperplane_opacities')
191
has_opacity = True
192
if not type(hyperplane_opacities) == list: # we assume a single number then
193
hyperplane_opacities = [hyperplane_opacities] * N
194
else:
195
has_opacity = False
196
point_sizes = kwds.pop('point_sizes', 50)
197
if not type(point_sizes) == list:
198
point_sizes = [point_sizes] * N
199
if kwds.has_key('ranges'):
200
ranges_set = True
201
ranges = kwds.pop('ranges')
202
if not type(ranges) in [list,tuple]: # ranges is a single number
203
ranges = [ranges] * N
204
# So ranges is some type of list.
205
elif dim == 2: # arrangement of lines in the plane
206
if not type(ranges[0]) in [list,tuple]: # a single interval
207
ranges = [ranges] * N
208
elif dim == 3: # arrangement of planes in 3-space
209
if not type(ranges[0][0]) in [list,tuple]:
210
ranges = [ranges] * N
211
elif dim not in [2,3]: # ranges is not an option unless dim is 2 or 3
212
ranges_set = False
213
else: # a list of intervals, one for each hyperplane is given
214
pass # ranges does not need to be modified
215
else:
216
ranges_set = False # give default values below
217
# the extra keywords have now been handled
218
# now handle the legend
219
if dim in [1,2]: # points on a line or lines in the plane
220
if hyperplane_legend in [True,'long']:
221
hyps = hyperplane_arrangement.hyperplanes()
222
legend_labels = [hyps[i]._latex_() for i in range(N)]
223
elif hyperplane_legend == 'short' :
224
legend_labels = [str(i) for i in range(N)]
225
else: # dim==3, arrangement of planes in 3-space
226
if hyperplane_legend in [True, 'long']:
227
legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'long')
228
elif hyperplane_legend == 'short':
229
legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'short')
230
## done handling the legend
231
## now create the plot
232
p = Graphics()
233
for i in range(N):
234
newk = copy(kwds)
235
if has_hyp_label:
236
newk['hyperplane_label'] = hyp_labels[i]
237
if has_offsets:
238
if type(offsets) != list:
239
newk['label_offset'] = offsets
240
else:
241
newk['label_offset'] = offsets[i]
242
else:
243
newk['hyperplane_label'] = False
244
if has_label_color:
245
newk['label_color'] = label_colors[i]
246
if has_label_fontsize:
247
newk['label_fontsize'] = label_fontsize[i]
248
if has_opacity:
249
newk['opacity'] = hyperplane_opacities[i]
250
if dim == 1:
251
newk['point_size'] = point_sizes[i]
252
if dim in [1,2] and hyperplane_legend != False: # more options than T/F
253
newk['legend_label'] = legend_labels[i]
254
if ranges_set:
255
newk['ranges'] = ranges[i]
256
p += plot_hyperplane(hyperplane_arrangement[i], rgbcolor=hyp_colors[i], **newk)
257
if dim == 1:
258
if hyperplane_legend != False: # there are more options than T/F
259
p.legend(True)
260
return p
261
elif dim == 2:
262
if hyperplane_legend != False: # there are more options than T/F
263
p.legend(True)
264
return p
265
else: # dim==3
266
if hyperplane_legend != False: # there are more options than T/F
267
return p, legend3d
268
else:
269
return p
270
271
272
273
274
275
def plot_hyperplane(hyperplane, **kwds):
276
r"""
277
Return the plot of a single hyperplane.
278
279
INPUT:
280
281
- ``**kwds`` -- plot options: see below
282
283
OUTPUT:
284
285
A graphics object of the plot.
286
287
.. RUBRIC:: Plot Options
288
289
Beside the usual plot options (enter ``plot?``), the plot command for
290
hyperplanes includes the following:
291
292
- ``hyperplane_label`` -- Boolean value or string (default: ``True``).
293
If ``True``, the hyperplane is labeled with its equation, if a
294
string, it is labeled by that string, otherwise it is not
295
labeled.
296
297
- ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label.
298
299
- ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14)
300
(does not work in 3d, yet).
301
302
- ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1),
303
2-dim: (0,0,0.2)) Amount by which label is offset from
304
``hyperplane.point()``.
305
306
- ``point_size`` -- (Default: 50) Size of points in a zero-dimensional
307
arrangement or of an arrangement over a finite field.
308
309
- ``ranges`` -- Range for the parameters for the parametric plot of the
310
hyperplane. If a single positive number ``r`` is given for the
311
value of ``ranges``, then the ranges for all parameters are set to
312
`[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the
313
form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the
314
``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]).
315
(The ranges are centered around ``hyperplane.point()``.)
316
317
EXAMPLES::
318
319
sage: H1.<x> = HyperplaneArrangements(QQ)
320
sage: a = 3*x + 4
321
sage: a.plot() # indirect doctest
322
sage: a.plot(point_size=100,hyperplane_label='hello')
323
324
325
sage: H2.<x,y> = HyperplaneArrangements(QQ)
326
sage: b = 3*x + 4*y + 5
327
sage: b.plot()
328
sage: b.plot(ranges=(1,5),label_offset=(2,-1))
329
sage: opts = {'hyperplane_label':True, 'label_color':'green',
330
....: 'label_fontsize':24, 'label_offset':(0,1.5)}
331
sage: b.plot(**opts)
332
333
sage: H3.<x,y,z> = HyperplaneArrangements(QQ)
334
sage: c = 2*x + 3*y + 4*z + 5
335
sage: c.plot()
336
sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False)
337
sage: d = -3*x + 2*y + 2*z + 3
338
sage: d.plot(opacity=0.8)
339
sage: e = 4*x + 2*z + 3
340
sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1)
341
"""
342
if hyperplane.base_ring().characteristic() != 0:
343
raise NotImplementedError('base field must have characteristic zero')
344
elif hyperplane.dimension() not in [0, 1, 2]: # dimension of hyperplane, not ambient space
345
raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3')
346
# handle extra keywords
347
if kwds.has_key('hyperplane_label'):
348
hyp_label = kwds.pop('hyperplane_label')
349
if hyp_label == False:
350
has_hyp_label = False
351
else:
352
has_hyp_label = True
353
else: # default
354
hyp_label = True
355
has_hyp_label = True
356
if has_hyp_label:
357
if hyp_label == True: # then label hyperplane with its equation
358
if hyperplane.dimension() == 2: # jmol does not like latex
359
label = hyperplane._repr_linear(include_zero=False)
360
else:
361
label = hyperplane._latex_()
362
else:
363
label = hyp_label # a string
364
if kwds.has_key('label_color'):
365
label_color = kwds.pop('label_color')
366
else:
367
label_color = 'black'
368
if kwds.has_key('label_fontsize'):
369
label_fontsize = kwds.pop('label_fontsize')
370
else:
371
label_fontsize = 14
372
if kwds.has_key('label_offset'):
373
has_offset = True
374
label_offset = kwds.pop('label_offset')
375
else:
376
has_offset = False # give default values below
377
if kwds.has_key('point_size'):
378
pt_size = kwds.pop('point_size')
379
else:
380
pt_size = 50
381
if kwds.has_key('ranges'):
382
ranges_set = True
383
ranges = kwds.pop('ranges')
384
else:
385
ranges_set = False # give default values below
386
# the extra keywords have now been handled
387
# now create the plot
388
if hyperplane.dimension() == 0: # a point on a line
389
x, = hyperplane.A()
390
d = hyperplane.b()
391
p = point((d/x,0), size = pt_size, **kwds)
392
if has_hyp_label:
393
if not has_offset:
394
label_offset = 0.1
395
p += text(label, (d/x,label_offset),
396
color=label_color,fontsize=label_fontsize)
397
p += text('',(d/x,label_offset+0.4)) # add space at top
398
if not kwds.has_key('ymax'):
399
kwds['ymax'] = 0.5
400
elif hyperplane.dimension() == 1: # a line in the plane
401
pnt = hyperplane.point()
402
w = hyperplane.linear_part().matrix()
403
x, y = hyperplane.A()
404
d = hyperplane.b()
405
t = SR.var('t')
406
if ranges_set:
407
if type(ranges) in [list,tuple]:
408
t0, t1 = ranges
409
else: # ranges should be a single positive number
410
t0, t1 = -ranges, ranges
411
else: # default
412
t0, t1 = -3, 3
413
p = parametric_plot(pnt+t*w[0], (t,t0,t1), **kwds)
414
if has_hyp_label:
415
if has_offset:
416
b0, b1 = label_offset
417
else:
418
b0, b1 = 0, 0.2
419
label = text(label,(pnt[0]+b0,pnt[1]+b1),
420
color=label_color,fontsize=label_fontsize)
421
p += label
422
elif hyperplane.dimension() == 2: # a plane in 3-space
423
pnt = hyperplane.point()
424
w = hyperplane.linear_part().matrix()
425
a, b, c = hyperplane.A()
426
d = hyperplane.b()
427
s,t = SR.var('s t')
428
if ranges_set:
429
if type(ranges) in [list,tuple]:
430
s0, s1 = ranges[0]
431
t0, t1 = ranges[1]
432
else: # ranges should be a single positive integers
433
s0, s1 = -ranges, ranges
434
t0, t1 = -ranges, ranges
435
else: # default
436
s0, s1 = -3, 3
437
t0, t1 = -3, 3
438
p = parametric_plot3d(pnt+s*w[0]+t*w[1],(s,s0,s1),(t,t0,t1),**kwds)
439
if has_hyp_label:
440
if has_offset:
441
b0, b1, b2 = label_offset
442
else:
443
b0, b1, b2 = 0, 0, 0
444
label = text3d(label,(pnt[0]+b0,pnt[1]+b1,pnt[2]+b2),
445
color=label_color,fontsize=label_fontsize)
446
p += label
447
return p
448
449
450
451
452
453
def legend_3d(hyperplane_arrangement, hyperplane_colors, length):
454
r"""
455
Create plot of a 3d legend for an arrangement of planes in 3-space. The
456
``length`` parameter determines whether short or long labels are used in
457
the legend.
458
459
INPUT:
460
461
- ``hyperplane_arrangement`` -- a hyperplane arrangement
462
463
- ``hyperplane_colors`` -- list of colors
464
465
- ``length`` -- either ``'short'`` or ``'long'``
466
467
OUTPUT:
468
469
- A graphics object.
470
471
EXAMPLES::
472
473
sage: a = hyperplane_arrangements.semiorder(3)
474
sage: from sage.geometry.hyperplane_arrangement.plot import legend_3d
475
sage: legend_3d(a, colors.values()[:6],length='long')
476
477
sage: b = hyperplane_arrangements.semiorder(4)
478
sage: c = b.essentialization()
479
sage: legend_3d(c, colors.values()[:12], length='long')
480
481
sage: legend_3d(c, colors.values()[:12], length='short')
482
483
sage: p = legend_3d(c, colors.values()[:12], length='short')
484
sage: p.set_legend_options(ncol=4)
485
sage: type(p)
486
<class 'sage.plot.graphics.Graphics'>
487
"""
488
if hyperplane_arrangement.dimension() != 3:
489
raise ValueError('arrangements must be in 3-space')
490
hyps = hyperplane_arrangement.hyperplanes()
491
N = len(hyperplane_arrangement)
492
if length == 'short':
493
labels = [' ' + str(i) for i in range(N)]
494
else:
495
labels = [' ' + hyps[i]._repr_linear(include_zero=False) for i in
496
range(N)]
497
p = Graphics()
498
for i in range(N):
499
p += line([(0,0),(0,0)], color=hyperplane_colors[i], thickness=8,
500
legend_label=labels[i], axes=False)
501
p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4,
502
fancybox=True, font_size='x-large', ncol=2)
503
p.legend(True)
504
return p
505
506
507