Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/toric_plotter.py
8815 views
1
r"""
2
Toric plotter
3
4
This module provides a helper class :class:`ToricPlotter` for producing plots
5
of objects related to toric geometry. Default plotting objects can be adjusted
6
using :func:`options` and reset using :func:`reset_options`.
7
8
AUTHORS:
9
10
- Andrey Novoseltsev (2010-10-03): initial version, using some code bits by
11
Volker Braun.
12
13
EXAMPLES:
14
15
In most cases, this module is used indirectly, e.g. ::
16
17
sage: fan = toric_varieties.dP6().fan()
18
sage: print fan.plot()
19
Graphics object consisting of 31 graphics primitives
20
21
You may change default plotting options as follows::
22
23
sage: toric_plotter.options("show_rays")
24
True
25
sage: toric_plotter.options(show_rays=False)
26
sage: toric_plotter.options("show_rays")
27
False
28
sage: print fan.plot()
29
Graphics object consisting of 19 graphics primitives
30
sage: toric_plotter.reset_options()
31
sage: toric_plotter.options("show_rays")
32
True
33
sage: print fan.plot()
34
Graphics object consisting of 31 graphics primitives
35
"""
36
37
38
#*****************************************************************************
39
# Copyright (C) 2010 Volker Braun <[email protected]>
40
# Copyright (C) 2010 Andrey Novoseltsev <[email protected]>
41
# Copyright (C) 2010 William Stein <[email protected]>
42
#
43
#
44
# Distributed under the terms of the GNU General Public License (GPL)
45
#
46
# http://www.gnu.org/licenses/
47
#*****************************************************************************
48
49
50
from copy import copy
51
from math import pi
52
53
from sage.functions.all import arccos, arctan2, ceil, floor
54
from sage.geometry.polyhedron.constructor import Polyhedron
55
from sage.modules.all import vector
56
from sage.plot.all import (Color, Graphics,
57
arrow, disk, line, point, polygon, rainbow, text)
58
from sage.plot.plot3d.all import text3d
59
from sage.rings.all import RDF
60
from sage.structure.sage_object import SageObject
61
62
63
# These options are used to initialize/reset plotting options.
64
# Most of them are set to "None" and "real default values" are computed
65
# automatically based on the plotted object and parameters actually provided by
66
# the user.
67
_default_options = dict()
68
_default_options["mode"] = "round" # Can be also "box" and "generators"
69
_default_options["show_lattice"] = None # Default is "True for small plots"
70
_default_options["show_rays"] = True
71
_default_options["show_generators"] = True
72
_default_options["show_walls"] = True
73
74
_default_options["generator_color"] = "blue"
75
_default_options["label_color"] = "black"
76
_default_options["point_color"] = "black"
77
_default_options["ray_color"] = "purple"
78
_default_options["wall_color"] = "rainbow"
79
_default_options["wall_alpha"] = 0.4
80
81
_default_options["point_size"] = None
82
_default_options["ray_thickness"] = 3
83
_default_options["generator_thickness"] = None
84
_default_options["font_size"] = 14
85
86
_default_options["ray_label"] = "u"
87
_default_options["wall_label"] = r"\sigma"
88
89
# If none of these are given, the default will be 2.5
90
_default_options["radius"] = None
91
_default_options["xmin"] = None
92
_default_options["xmax"] = None
93
_default_options["ymin"] = None
94
_default_options["ymax"] = None
95
_default_options["zmin"] = None
96
_default_options["zmax"] = None
97
98
_default_options["lattice_filter"] = None
99
100
_default_options["wall_zorder"] = -5
101
_default_options["ray_zorder"] = -4
102
_default_options["generator_zorder"] = -3
103
_default_options["point_zorder"] = -2
104
_default_options["label_zorder"] = -1
105
106
# These options are actually used as "current defaults" in plotting functions.
107
_options = copy(_default_options)
108
109
110
class ToricPlotter(SageObject):
111
r"""
112
Create a toric plotter.
113
114
INPUT:
115
116
- ``all_options`` -- a :class:`dictionary <dict>`, containing any of the
117
options related to toric objects (see :func:`options`) and any other
118
options that will be passed to lower level plotting functions;
119
120
- ``dimension`` -- an integer (1, 2, or 3), dimension of toric objects to
121
be plotted;
122
123
- ``generators`` -- (optional) a list of ray generators, see examples for
124
a detailed explanation of this argument.
125
126
OUTPUT:
127
128
- a toric plotter.
129
130
EXAMPLES:
131
132
In most cases there is no need to create and use :class:`ToricPlotter`
133
directly. Instead, use plotting method of the object which you want to
134
plot, e.g. ::
135
136
sage: fan = toric_varieties.dP6().fan()
137
sage: fan.plot()
138
sage: print fan.plot()
139
Graphics object consisting of 31 graphics primitives
140
141
If you do want to create your own plotting function for some toric
142
structure, the anticipated usage of toric plotters is the following:
143
144
- collect all necessary options in a dictionary;
145
146
- pass these options and ``dimension`` to :class:`ToricPlotter`;
147
148
- call :meth:`include_points` on ray generators and any other points that
149
you want to be present on the plot (it will try to set appropriate
150
cut-off bounds);
151
152
- call :meth:`adjust_options` to choose "nice" default values for all
153
options that were not set yet and ensure consistency of rectangular and
154
spherical cut-off bounds;
155
156
- call :meth:`set_rays` on ray generators to scale them to the cut-off
157
bounds of the plot;
158
159
- call appropriate ``plot_*`` functions to actually construct the plot.
160
161
For example, the plot from the previous example can be obtained as
162
follows::
163
164
sage: from sage.geometry.toric_plotter import ToricPlotter
165
sage: options = dict() # use default for everything
166
sage: tp = ToricPlotter(options, fan.lattice().degree())
167
sage: tp.include_points(fan.rays())
168
sage: tp.adjust_options()
169
sage: tp.set_rays(fan.rays())
170
sage: result = tp.plot_lattice()
171
sage: result += tp.plot_rays()
172
sage: result += tp.plot_generators()
173
sage: result += tp.plot_walls(fan(2))
174
sage: print result
175
Graphics object consisting of 31 graphics primitives
176
177
In most situations it is only necessary to include generators of rays, in
178
this case they can be passed to the constructor as an optional argument.
179
In the example above, the toric plotter can be completely set up using ::
180
181
sage: tp = ToricPlotter(options, fan.lattice().degree(), fan.rays())
182
183
All options are exposed as attributes of toric plotters and can be modified
184
after constructions, however you will have to manually call
185
:meth:`adjust_options` and :meth:`set_rays` again if you decide to change
186
the plotting mode and/or cut-off bounds. Otherwise plots maybe invalid.
187
"""
188
189
def __init__(self, all_options, dimension, generators=None):
190
r"""
191
See :class:`ToricPlotter` for documentation.
192
193
TESTS::
194
195
sage: from sage.geometry.toric_plotter import ToricPlotter
196
sage: tp = ToricPlotter(dict(), 2)
197
sage: TestSuite(tp).run()
198
"""
199
super(ToricPlotter, self).__init__()
200
sd = self.__dict__
201
extra_options = dict()
202
self.extra_options = extra_options
203
toric_options = options()
204
for option, value in all_options.iteritems():
205
if option in toric_options:
206
sd[option] = value
207
else:
208
extra_options[option] = value
209
for option, value in toric_options.iteritems():
210
if option not in sd:
211
sd[option] = value
212
if dimension not in [1, 2, 3]:
213
raise ValueError("toric objects can be plotted only for "
214
"dimensions 1, 2, and 3, not %s!" % dimension)
215
self.dimension = dimension
216
self.origin = vector(RDF, max(dimension, 2)) # 1-d is plotted in 2-d
217
if self.mode not in ["box", "generators", "round"]:
218
raise ValueError("unrecognized plotting mode: %s!" % mode)
219
# If radius was explicitly set by the user, it sets other bounds too.
220
# If we don't take it into account here, they will be replaced by
221
# automatically computed values.
222
if sd["radius"] is not None:
223
for key in ["xmin", "ymin", "zmin"]:
224
if sd[key] is None:
225
sd[key] = - sd["radius"]
226
for key in ["xmax", "ymax", "zmax"]:
227
if sd[key] is None:
228
sd[key] = sd["radius"]
229
# We also set some of the "extra_options" if they were not given.
230
if "axes" not in extra_options:
231
extra_options["axes"] = False
232
if "frame" not in extra_options:
233
extra_options["frame"] = False
234
if "aspect_ratio" not in extra_options:
235
extra_options["aspect_ratio"] = 1
236
if generators is not None:
237
# Completely prepare the plotter
238
self.include_points(generators)
239
self.adjust_options()
240
self.set_rays(generators)
241
242
def __eq__(self, other):
243
r"""
244
Check if ``self`` is equal to ``other``.
245
246
INPUT:
247
248
- ``other`` -- anything.
249
250
OUTPUT:
251
252
- ``True`` if ``self`` is equal to ``other``, ``False`` otherwise.
253
254
TESTS::
255
256
sage: from sage.geometry.toric_plotter import ToricPlotter
257
sage: ToricPlotter(dict(), 2) == ToricPlotter(dict(), 2)
258
True
259
sage: ToricPlotter(dict(), 2) == 0
260
False
261
"""
262
# Just to make TestSuite happy...
263
return self.__dict__ == other.__dict__
264
265
def adjust_options(self):
266
r"""
267
Adjust plotting options.
268
269
This function determines appropriate default values for those options,
270
that were not specified by the user, based on the other options. See
271
:class:`ToricPlotter` for a detailed example.
272
273
OUTPUT:
274
275
- none.
276
277
TESTS::
278
279
sage: from sage.geometry.toric_plotter import ToricPlotter
280
sage: tp = ToricPlotter(dict(), 2)
281
sage: print tp.show_lattice
282
None
283
sage: tp.adjust_options()
284
sage: print tp.show_lattice
285
True
286
"""
287
# Unfortunately, some of the defaults are dimension specific for no
288
# good reason but to remedy 2-d/3-d plotting inconsistencies in Sage.
289
d = self.dimension
290
if d <= 2:
291
if self.point_size is None:
292
self.point_size = 50
293
elif d == 3:
294
if self.point_size is None:
295
self.point_size = 10
296
if self.generator_thickness is None:
297
self.generator_thickness = self.ray_thickness
298
sd = self.__dict__
299
bounds = ["radius", "xmin", "xmax", "ymin", "ymax", "zmin", "zmax"]
300
bounds = [abs(sd[bound]) for bound in bounds if sd[bound] is not None]
301
r = RDF(max(bounds + [0.5]) if bounds else 2.5)
302
self.radius = r
303
round = self.mode == "round"
304
for key in ["xmin", "ymin", "zmin"]:
305
if round or sd[key] is None:
306
sd[key] = - r
307
if sd[key] > - 0.5:
308
sd[key] = - 0.5
309
sd[key] = RDF(sd[key])
310
for key in ["xmax", "ymax", "zmax"]:
311
if round or sd[key] is None:
312
sd[key] = r
313
if sd[key] < 0.5:
314
sd[key] = 0.5
315
sd[key] = RDF(sd[key])
316
if self.show_lattice is None:
317
self.show_lattice = (r <= 5) if d <= 2 else r <= 3
318
319
def include_points(self, points, force=False):
320
r"""
321
Try to include ``points`` into the bounding box of ``self``.
322
323
INPUT:
324
325
- ``points`` -- a list of points;
326
327
- ``force`` -- boolean (default: ``False``). by default, only bounds
328
that were not set before will be chosen to include ``points``. Use
329
``force=True`` if you don't mind increasing existing bounding box.
330
331
OUTPUT:
332
333
- none.
334
335
EXAMPLES::
336
337
sage: from sage.geometry.toric_plotter import ToricPlotter
338
sage: tp = ToricPlotter(dict(), 2)
339
sage: print tp.radius
340
None
341
sage: tp.include_points([(3, 4)])
342
sage: print tp.radius
343
5.5...
344
sage: tp.include_points([(5, 12)])
345
sage: print tp.radius
346
5.5...
347
sage: tp.include_points([(5, 12)], force=True)
348
sage: print tp.radius
349
13.5...
350
"""
351
if not points:
352
return
353
points = [vector(RDF, pt) for pt in points]
354
sd = self.__dict__
355
356
def update(bound, new_value, points):
357
if force or sd[bound] is None:
358
new_value = eval(new_value)
359
if sd[bound] is None:
360
sd[bound] = new_value
361
elif abs(sd[bound]) < abs(new_value):
362
sd[bound] = new_value
363
364
update("radius", "max(pt.norm() for pt in points) + 0.5", points)
365
try:
366
update("xmin", "min(pt[0] for pt in points) - 0.5", points)
367
update("xmax", "max(pt[0] for pt in points) + 0.5", points)
368
update("ymin", "min(pt[1] for pt in points) - 0.5", points)
369
update("ymax", "max(pt[1] for pt in points) + 0.5", points)
370
update("zmin", "min(pt[2] for pt in points) - 0.5", points)
371
update("zmax", "max(pt[2] for pt in points) + 0.5", points)
372
except IndexError: # 1 or 2 dimensional case
373
pass
374
375
def plot_generators(self):
376
r"""
377
Plot ray generators.
378
379
Ray generators must be specified during construction or using
380
:meth:`set_rays` before calling this method.
381
382
OUTPUT:
383
384
- a plot.
385
386
EXAMPLES::
387
388
sage: from sage.geometry.toric_plotter import ToricPlotter
389
sage: tp = ToricPlotter(dict(), 2, [(3,4)])
390
sage: print tp.plot_generators()
391
Graphics object consisting of 1 graphics primitive
392
"""
393
generators = self.generators
394
result = Graphics()
395
if not generators or not self.show_generators:
396
return result
397
colors = color_list(self.generator_color, len(generators))
398
d = self.dimension
399
extra_options = self.extra_options
400
origin = self.origin
401
thickness = self.generator_thickness
402
zorder = self.generator_zorder
403
for generator, ray, color in zip(generators, self.rays, colors):
404
if ray.norm() < generator.norm():
405
result += line([origin, ray],
406
color=color, thickness=thickness,
407
zorder=zorder, **extra_options)
408
else:
409
# This should not be the case, but as of 4.6 plotting
410
# functions are inconsistent and arrows behave very
411
# different compared to lines.
412
if d <= 2:
413
result += arrow(origin, generator,
414
color=color, width=thickness,
415
arrowsize=thickness + 1,
416
zorder=zorder, **extra_options)
417
else:
418
result += line([origin, generator], arrow_head=True,
419
color=color, thickness=thickness,
420
zorder=zorder, **extra_options)
421
return result
422
423
def plot_labels(self, labels, positions):
424
r"""
425
Plot ``labels`` at specified ``positions``.
426
427
INPUT:
428
429
- ``labels`` -- a string or a list of strings;
430
431
- ``positions`` -- a list of points.
432
433
OUTPUT:
434
435
- a plot.
436
437
EXAMPLES::
438
439
sage: from sage.geometry.toric_plotter import ToricPlotter
440
sage: tp = ToricPlotter(dict(), 2)
441
sage: print tp.plot_labels("u", [(1.5,0)])
442
Graphics object consisting of 1 graphics primitive
443
"""
444
result = Graphics()
445
color = self.label_color
446
extra_options = self.extra_options
447
zorder = self.label_zorder
448
font_size = self.font_size
449
twod = self.dimension <= 2
450
labels = label_list(labels, len(positions), twod)
451
for label, position in zip(labels, positions):
452
if label is None:
453
continue
454
if twod:
455
result += text(label, position,
456
color=color, fontsize=font_size,
457
zorder=zorder, **extra_options)
458
else:
459
result += text3d(label, position, color=color, **extra_options)
460
return result
461
462
def plot_lattice(self):
463
r"""
464
Plot the lattice (i.e. its points in the cut-off bounds of ``self``).
465
466
OUTPUT:
467
468
- a plot.
469
470
EXAMPLES::
471
472
sage: from sage.geometry.toric_plotter import ToricPlotter
473
sage: tp = ToricPlotter(dict(), 2)
474
sage: print tp.plot_lattice()
475
Graphics object consisting of 1 graphics primitive
476
"""
477
if not self.show_lattice:
478
# Plot the origin anyway, otherwise rays/generators may look ugly.
479
return self.plot_points([self.origin])
480
d = self.dimension
481
extra_options = self.extra_options
482
if d == 1:
483
points = ((x, 0)
484
for x in range(ceil(self.xmin), floor(self.xmax) + 1))
485
elif d == 2:
486
points = ((x, y)
487
for x in range(ceil(self.xmin), floor(self.xmax) + 1)
488
for y in range(ceil(self.ymin), floor(self.ymax) + 1))
489
elif d == 3:
490
points = ((x, y, z)
491
for x in range(ceil(self.xmin), floor(self.xmax) + 1)
492
for y in range(ceil(self.ymin), floor(self.ymax) + 1)
493
for z in range(ceil(self.zmin), floor(self.zmax) + 1))
494
if self.mode == "round":
495
r = 1.01 * self.radius # To make sure integer values work OK.
496
points = (pt for pt in points if vector(pt).norm() <= r)
497
f = self.lattice_filter
498
if f is not None:
499
points = (pt for pt in points if f(pt))
500
return self.plot_points(tuple(points))
501
502
def plot_points(self, points):
503
r"""
504
Plot given ``points``.
505
506
INPUT:
507
508
- ``points`` -- a list of points.
509
510
OUTPUT:
511
512
- a plot.
513
514
EXAMPLES::
515
516
sage: from sage.geometry.toric_plotter import ToricPlotter
517
sage: tp = ToricPlotter(dict(), 2)
518
sage: print tp.plot_points([(1,0), (0,1)])
519
Graphics object consisting of 1 graphics primitive
520
"""
521
return point(points, color=self.point_color, size=self.point_size,
522
zorder=self.point_zorder, **self.extra_options)
523
524
def plot_ray_labels(self):
525
r"""
526
Plot ray labels.
527
528
Usually ray labels are plotted together with rays, but in some cases it
529
is desirable to output them separately.
530
531
Ray generators must be specified during construction or using
532
:meth:`set_rays` before calling this method.
533
534
OUTPUT:
535
536
- a plot.
537
538
EXAMPLES::
539
540
sage: from sage.geometry.toric_plotter import ToricPlotter
541
sage: tp = ToricPlotter(dict(), 2, [(3,4)])
542
sage: print tp.plot_ray_labels()
543
Graphics object consisting of 1 graphics primitive
544
"""
545
return self.plot_labels(self.ray_label,
546
[1.1 * ray for ray in self.rays])
547
548
def plot_rays(self):
549
r"""
550
Plot rays and their labels.
551
552
Ray generators must be specified during construction or using
553
:meth:`set_rays` before calling this method.
554
555
OUTPUT:
556
557
- a plot.
558
559
EXAMPLES::
560
561
sage: from sage.geometry.toric_plotter import ToricPlotter
562
sage: tp = ToricPlotter(dict(), 2, [(3,4)])
563
sage: print tp.plot_rays()
564
Graphics object consisting of 2 graphics primitives
565
"""
566
result = Graphics()
567
rays = self.rays
568
if not rays or not self.show_rays:
569
return result
570
extra_options = self.extra_options
571
origin = self.origin
572
colors = color_list(self.ray_color, len(rays))
573
thickness = self.ray_thickness
574
zorder = self.ray_zorder
575
for end, color in zip(rays, colors):
576
result += line([origin, end],
577
color=color, thickness=thickness,
578
zorder=zorder, **extra_options)
579
result += self.plot_ray_labels()
580
return result
581
582
def plot_walls(self, walls):
583
r"""
584
Plot ``walls``, i.e. 2-d cones, and their labels.
585
586
Ray generators must be specified during construction or using
587
:meth:`set_rays` before calling this method and these specified
588
ray generators will be used in conjunction with
589
:meth:`~sage.geometry.cone.ConvexRationalPolyhedralCone.ambient_ray_indices`
590
of ``walls``.
591
592
INPUT:
593
594
- ``walls`` -- a list of 2-d cones.
595
596
OUTPUT:
597
598
- a plot.
599
600
EXAMPLES::
601
602
sage: quadrant = Cone([(1,0), (0,1)])
603
sage: from sage.geometry.toric_plotter import ToricPlotter
604
sage: tp = ToricPlotter(dict(), 2, quadrant.rays())
605
sage: print tp.plot_walls([quadrant])
606
Graphics object consisting of 2 graphics primitives
607
"""
608
result = Graphics()
609
if not walls or not self.show_walls:
610
return result
611
rays = self.rays
612
extra_options = self.extra_options
613
mode = self.mode
614
alpha = self.wall_alpha
615
colors = color_list(self.wall_color, len(walls))
616
zorder = self.wall_zorder
617
if mode == "box":
618
if self.dimension <= 2:
619
ieqs = [(self.xmax, -1, 0), (- self.xmin, 1, 0),
620
(self.ymax, 0, -1), (- self.ymin, 0, 1)]
621
else:
622
ieqs = [(self.xmax, -1, 0, 0), (- self.xmin, 1, 0, 0),
623
(self.ymax, 0, -1, 0), (- self.ymin, 0, 1, 0),
624
(self.zmax, 0, 0, -1), (- self.zmin, 0, 0, 1)]
625
box = Polyhedron(ieqs=ieqs, field=RDF)
626
for wall, color in zip(walls, colors):
627
result += box.intersection(wall.polyhedron()).render_solid(
628
alpha=alpha, color=color, zorder=zorder, **extra_options)
629
elif mode == "generators":
630
origin = self.origin
631
for wall, color in zip(walls, colors):
632
vertices = [rays[i] for i in wall.ambient_ray_indices()]
633
vertices.append(origin)
634
result += Polyhedron(vertices=vertices, field=RDF).render_solid(
635
alpha=alpha, color=color, zorder=zorder, **extra_options)
636
label_sectors = []
637
round = mode == "round"
638
for wall, color in zip(walls, colors):
639
S = wall.linear_subspace()
640
lsd = S.dimension()
641
if lsd == 0: # Strictly convex wall
642
r1, r2 = (rays[i] for i in wall.ambient_ray_indices())
643
elif lsd == 1: # wall is a half-plane
644
for i, ray in zip(wall.ambient_ray_indices(), wall.rays()):
645
if ray in S:
646
r1 = rays[i]
647
else:
648
r2 = rays[i]
649
if round:
650
# Plot one "extra" sector
651
result += sector(- r1, r2,
652
alpha=alpha, color=color, zorder=zorder, **extra_options)
653
else: # wall is a plane
654
r1, r2 = S.basis()
655
r1 = vector(RDF, r1)
656
r1 = r1 / r1.norm() * self.radius
657
r2 = vector(RDF, r2)
658
r2 = r2 / r2.norm() * self.radius
659
if round:
660
# Plot three "extra" sectors
661
result += sector(r1, - r2,
662
alpha=alpha, color=color, zorder=zorder, **extra_options)
663
result += sector(- r1, r2,
664
alpha=alpha, color=color, zorder=zorder, **extra_options)
665
result += sector(- r1, - r2,
666
alpha=alpha, color=color, zorder=zorder, **extra_options)
667
label_sectors.append([r1, r2])
668
if round:
669
result += sector(r1, r2,
670
alpha=alpha, color=color, zorder=zorder, **extra_options)
671
result += self.plot_labels(self.wall_label,
672
[sum(label_sector) / 3 for label_sector in label_sectors])
673
return result
674
675
def set_rays(self, generators):
676
r"""
677
Set up rays and their ``generators`` to be used by plotting functions.
678
679
As an alternative to using this method, you can pass ``generators`` to
680
:class:`ToricPlotter` constructor.
681
682
INPUT:
683
684
- ``generators`` - a list of primitive non-zero ray generators.
685
686
OUTPUT:
687
688
- none.
689
690
EXAMPLES::
691
692
sage: from sage.geometry.toric_plotter import ToricPlotter
693
sage: tp = ToricPlotter(dict(), 2)
694
sage: tp.adjust_options()
695
sage: print tp.plot_rays()
696
Traceback (most recent call last):
697
...
698
AttributeError: 'ToricPlotter' object has no attribute 'rays'
699
sage: tp.set_rays([(0,1)])
700
sage: print tp.plot_rays()
701
Graphics object consisting of 2 graphics primitives
702
"""
703
d = self.dimension
704
if d == 1:
705
generators = [vector(RDF, 2, (gen[0], 0)) for gen in generators]
706
else:
707
generators = [vector(RDF, d, gen) for gen in generators]
708
self.generators = generators
709
if self.mode == "box":
710
rays = []
711
bounds = [self.__dict__[bound]
712
for bound in ["xmin", "xmax", "ymin", "ymax", "zmin", "zmax"]]
713
bounds = bounds[:2 * d]
714
for gen in generators:
715
factors = []
716
for i, gen_i in enumerate(gen):
717
factors.append(gen_i / bounds[2 * i])
718
factors.append(gen_i / bounds[2 * i + 1])
719
rays.append(gen / max(factors))
720
elif self.mode == "generators":
721
rays = generators
722
elif self.mode == "round":
723
r = self.radius
724
rays = [r * gen / gen.norm() for gen in generators]
725
self.rays = rays
726
727
728
def _unrecognized_option(option):
729
r"""
730
Raise an exception about wrong ``option``.
731
732
INPUT:
733
734
- ``option`` -- a string.
735
736
OUTPUT:
737
738
- none, a ``KeyError`` exception is raised.
739
740
TESTS::
741
742
sage: from sage.geometry.toric_plotter import _unrecognized_option
743
sage: _unrecognized_option("nontoric")
744
Traceback (most recent call last):
745
...
746
KeyError: "unrecognized toric plot option: 'nontoric'!
747
Type 'toric_plotter.options?' to see available options."
748
"""
749
raise KeyError("unrecognized toric plot option: '%s'! " % option
750
+ "Type 'toric_plotter.options?' to see available options.")
751
752
753
def color_list(color, n):
754
r"""
755
Normalize a list of ``n`` colors.
756
757
INPUT:
758
759
- ``color`` -- anything specifying a :class:`Color`, a list of such
760
specifications, or the string "rainbow";
761
762
- ``n`` - an integer.
763
764
OUTPUT:
765
766
- a list of ``n`` colors.
767
768
If ``color`` specified a single color, it is repeated ``n`` times. If it
769
was a list of ``n`` colors, it is returned without changes. If it was
770
"rainbow", the rainbow of ``n`` colors is returned.
771
772
EXAMPLES::
773
774
sage: from sage.geometry.toric_plotter import color_list
775
sage: color_list("grey", 1)
776
[RGB color (0.5019607843137255, 0.5019607843137255, 0.5019607843137255)]
777
sage: len(color_list("grey", 3))
778
3
779
sage: L = color_list("rainbow", 3)
780
sage: L
781
[RGB color (1.0, 0.0, 0.0),
782
RGB color (0.0, 1.0, 0.0),
783
RGB color (0.0, 0.0, 1.0)]
784
sage: color_list(L, 3)
785
[RGB color (1.0, 0.0, 0.0),
786
RGB color (0.0, 1.0, 0.0),
787
RGB color (0.0, 0.0, 1.0)]
788
sage: color_list(L, 4)
789
Traceback (most recent call last):
790
...
791
ValueError: expected 4 colors, got 3!
792
"""
793
try:
794
color = Color(color)
795
return [color] * n
796
except (ValueError, TypeError):
797
if isinstance(color, (list, tuple)):
798
if len(color) != n:
799
raise ValueError("expected %d colors, got %d!"
800
% (n, len(color)))
801
return color
802
if color == "rainbow":
803
return [Color(c) for c in rainbow(n, "rgbtuple")]
804
raise TypeError("cannot interpret %s as a color!" % color)
805
806
807
def label_list(label, n, math_mode, index_set=None):
808
r"""
809
Normalize a list of ``n`` labels.
810
811
INPUT:
812
813
- ``label`` -- ``None``, a string, or a list of string;
814
815
- ``n`` - an integer;
816
817
- ``math_mode`` -- boolean, if ``True``, will produce LaTeX expressions
818
for labels;
819
820
- ``index_set`` -- a list of integers (default: ``range(n)``) that will be
821
used as subscripts for labels.
822
823
OUTPUT:
824
825
- a list of ``n`` labels.
826
827
If ``label`` was a list of ``n`` entries, it is returned without changes.
828
If ``label`` is ``None``, a list of ``n`` ``None``'s is returned. If
829
``label`` is a string, a list of strings of the form "$label_{i}$" is
830
returned, where `i` ranges over ``index_set``. (If ``math_mode=False``, the
831
form "label_i" is used instead.) If ``n=1``, there is no subscript added,
832
unless ``index_set`` was specified explicitly.
833
834
EXAMPLES::
835
836
sage: from sage.geometry.toric_plotter import label_list
837
sage: label_list("u", 3, False)
838
['u_0', 'u_1', 'u_2']
839
sage: label_list("u", 3, True)
840
['$u_{0}$', '$u_{1}$', '$u_{2}$']
841
sage: label_list("u", 1, True)
842
['$u$']
843
"""
844
if label is None:
845
return [None] * n
846
if isinstance(label, (list, tuple)):
847
if len(label) != n:
848
raise ValueError("expected %d labels, got %d!" % (n, len(label)))
849
return label
850
if index_set is None:
851
if n == 1:
852
return ["$%s$" % label.strip("$")] if math_mode else [label]
853
index_set = range(n)
854
if math_mode:
855
label = label.strip("$")
856
return list("$%s_{%d}$" % (label, i) for i in index_set)
857
else:
858
return list("%s_%d" % (label, i) for i in index_set)
859
860
861
def options(option=None, **kwds):
862
r"""
863
Get or set options for plots of toric geometry objects.
864
865
.. NOTE::
866
867
This function provides access to global default options. Any of these
868
options can be overridden by passing them directly to plotting
869
functions. See also :func:`reset_options`.
870
871
INPUT:
872
873
- None;
874
875
OR:
876
877
- ``option`` -- a string, name of the option whose value you wish to get;
878
879
OR:
880
881
- keyword arguments specifying new values for one or more options.
882
883
OUTPUT:
884
885
- if there was no input, the dictionary of current options for toric plots;
886
887
- if ``option`` argument was given, the current value of ``option``;
888
889
- if other keyword arguments were given, none.
890
891
**Name Conventions**
892
893
To clearly distinguish parts of toric plots, in options and methods we use
894
the following name conventions:
895
896
Generator
897
A primitive integral vector generating a 1-dimensional cone, plotted as
898
an arrow from the origin (or a line, if the head of the arrow is beyond
899
cut-off bounds for the plot).
900
901
Ray
902
A 1-dimensional cone, plotted as a line from the origin to the cut-off
903
bounds for the plot.
904
905
Wall
906
A 2-dimensional cone, plotted as a region between rays (in the above
907
sense). Its exact shape depends on the plotting mode (see below).
908
909
Chamber
910
A 3-dimensional cone, plotting is not implemented yet.
911
912
**Plotting Modes**
913
914
A plotting mode mostly determines the shape of the cut-off region (which is
915
always relevant for toric plots except for trivial objects consisting of
916
the origin only). The following options are available:
917
918
Box
919
The cut-off region is a box with edges parallel to coordinate axes.
920
921
Generators
922
The cut-off region is determined by primitive integral generators of
923
rays. Note that this notion is well-defined only for rays and walls,
924
in particular you should plot the lattice on your own
925
(:meth:`~ToricPlotter.plot_lattice` will use box mode which is likely
926
to be unsuitable). While this method may not be suitable for general
927
fans, it is quite natural for fans of :class:`CPR-Fano toric varieties.
928
<sage.schemes.toric.fano_variety.CPRFanoToricVariety_field`
929
930
Round
931
The cut-off regions is a sphere centered at the origin.
932
933
**Available Options**
934
935
Default values for the following options can be set using this function:
936
937
938
- ``mode`` -- "box", "generators", or "round", see above for descriptions;
939
940
- ``show_lattice`` -- boolean, whether to show lattice points in the
941
cut-off region or not;
942
943
- ``show_rays`` -- boolean, whether to show rays or not;
944
945
- ``show_generators`` -- boolean, whether to show rays or not;
946
947
- ``show_walls`` -- boolean, whether to show rays or not;
948
949
- ``generator_color`` -- a color for generators;
950
951
- ``label_color`` -- a color for labels;
952
953
- ``point_color`` -- a color for lattice points;
954
955
- ``ray_color`` -- a color for rays, a list of colors (one for each ray),
956
or the string "rainbow";
957
958
- ``wall_color`` -- a color for walls, a list of colors (one for each
959
wall), or the string "rainbow";
960
961
- ``wall_alpha`` -- a number between 0 and 1, the alpha-value for walls
962
(determining their transparency);
963
964
- ``point_size`` -- an integer, the size of lattice points;
965
966
- ``ray_thickness`` -- an integer, the thickness of rays;
967
968
- ``generator_thickness`` -- an integer, the thickness of generators;
969
970
- ``font_size`` -- an integer, the size of font used for labels;
971
972
- ``ray_label`` -- a string or a list of strings used for ray labels;
973
974
- ``wall_label`` -- a string or a list of strings used for wall labels;
975
976
- ``radius`` -- a positive number, the radius of the cut-off region for
977
"round" mode;
978
979
- ``xmin``, ``xmax``, ``ymin``, ``ymax``, ``zmin``, ``zmax`` -- numbers
980
determining the cut-off region for "box" mode. Note that you cannot
981
exclude the origin - if you try to do so, bounds will be automatically
982
expanded to include it;
983
984
- ``lattice_filter`` -- a callable, taking as an argument a lattice point
985
and returning ``True`` if this point should be included on the plot
986
(useful, e.g. for plotting sublattices);
987
988
- ``wall_zorder``, ``ray_zorder``, ``generator_zorder``, ``point_zorder``,
989
``label_zorder`` -- integers, z-orders for different classes of objects.
990
By default all values are negative, so that you can add other graphic
991
objects on top of a toric plot. You may need to adjust these parameters
992
if you want to put a toric plot on top of something else or if you want
993
to overlap several toric plots.
994
995
You can see the current default value of any options by typing, e.g. ::
996
997
sage: toric_plotter.options("show_rays")
998
True
999
1000
If the default value is ``None``, it means that the actual default is
1001
determined later based on the known options. Note, that not all options can
1002
be determined in such a way, so you should not set options to ``None``
1003
unless it was its original state. (You can always revert to this "original
1004
state" using :meth:`reset_options`.)
1005
1006
EXAMPLES:
1007
1008
The following line will make all subsequent toric plotting commands to draw
1009
"rainbows" from walls::
1010
1011
sage: toric_plotter.options(wall_color="rainbow")
1012
1013
If you prefer a less colorful output (e.g. if you need black-and-white
1014
illustrations for a paper), you can use something like this::
1015
1016
sage: toric_plotter.options(wall_color="grey")
1017
"""
1018
global _options
1019
if option is None and not kwds:
1020
return copy(_options)
1021
elif option is not None and not kwds:
1022
try:
1023
return _options[option]
1024
except KeyError:
1025
_unrecognized_option(option)
1026
elif option is None and kwds:
1027
for option in kwds:
1028
try:
1029
_options[option] = kwds[option]
1030
except KeyError:
1031
_unrecognized_option(option)
1032
else:
1033
raise ValueError("you cannot specify 'option' and other arguments at "
1034
"the same time!")
1035
1036
1037
def reset_options():
1038
r"""
1039
Reset options for plots of toric geometry objects.
1040
1041
OUTPUT:
1042
1043
- none.
1044
1045
EXAMPLES::
1046
1047
sage: toric_plotter.options("show_rays")
1048
True
1049
sage: toric_plotter.options(show_rays=False)
1050
sage: toric_plotter.options("show_rays")
1051
False
1052
1053
Now all toric plots will not show rays, unless explicitly requested. If you
1054
want to go back to "default defaults", use this method::
1055
1056
sage: toric_plotter.reset_options()
1057
sage: toric_plotter.options("show_rays")
1058
True
1059
"""
1060
global _options
1061
_options = copy(_default_options)
1062
1063
1064
def sector(ray1, ray2, **extra_options):
1065
r"""
1066
Plot a sector between ``ray1`` and ``ray2`` centered at the origin.
1067
1068
.. NOTE::
1069
1070
This function was intended for plotting strictly convex cones, so it
1071
plots the smaller sector between ``ray1`` and ``ray2`` and, therefore,
1072
they cannot be opposite. If you do want to use this function for bigger
1073
regions, split them into several parts.
1074
1075
.. NOTE::
1076
1077
As of version 4.6 Sage does not have a graphic primitive for sectors in
1078
3-dimensional space, so this function will actually approximate them
1079
using polygons (the number of vertices used depends on the angle
1080
between rays).
1081
1082
INPUT:
1083
1084
- ``ray1``, ``ray2`` -- rays in 2- or 3-dimensional space of the same
1085
length;
1086
1087
- ``extra_options`` -- a dictionary of options that should be passed to
1088
lower level plotting functions.
1089
1090
OUTPUT:
1091
1092
- a plot.
1093
1094
EXAMPLES::
1095
1096
sage: from sage.geometry.toric_plotter import sector
1097
sage: print sector((1,0), (0,1))
1098
Graphics object consisting of 1 graphics primitive
1099
sage: print sector((3,2,1), (1,2,3))
1100
Graphics3d Object
1101
"""
1102
ray1 = vector(RDF, ray1)
1103
ray2 = vector(RDF, ray2)
1104
r = ray1.norm()
1105
if len(ray1) == 2:
1106
# Plot an honest sector
1107
phi1 = arctan2(ray1[1], ray1[0])
1108
phi2 = arctan2(ray2[1], ray2[0])
1109
if phi1 > phi2:
1110
phi1, phi2 = phi2, phi1
1111
if phi2 - phi1 > pi:
1112
phi1, phi2 = phi2, phi1 + 2 * pi
1113
return disk((0,0), r, (phi1, phi2), **extra_options)
1114
else:
1115
# Plot a polygon, 30 vertices per radian.
1116
vertices_per_radian = 30
1117
n = ceil(arccos(ray1 * ray2 / r**2) * vertices_per_radian)
1118
dr = (ray2 - ray1) / n
1119
points = (ray1 + i * dr for i in range(n + 1))
1120
points = [r / pt.norm() * pt for pt in points]
1121
points.append(vector(RDF, 3))
1122
return polygon(points, **extra_options)
1123
1124