Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/toric_plotter.py
4045 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: color_list("rainbow", 3)
780
[RGB color (1.0, 0.0, 0.0),
781
RGB color (0.0, 1.0, 0.0),
782
RGB color (0.0, 0.0, 1.0)]
783
"""
784
try:
785
color = Color(color)
786
return [color] * n
787
except ValueError, TypeError:
788
if isinstance(color, (list, tuple)):
789
if len(color) != n:
790
raise ValueError("expected %d colors, got %d!"
791
% (n, len(label)))
792
return color
793
if color == "rainbow":
794
return [Color(c) for c in rainbow(n, "rgbtuple")]
795
raise TypeError("cannot interpret %s as a color!" % color)
796
797
798
def label_list(label, n, math_mode, index_set=None):
799
r"""
800
Normalize a list of ``n`` labels.
801
802
INPUT:
803
804
- ``label`` -- ``None``, a string, or a list of string;
805
806
- ``n`` - an integer;
807
808
- ``math_mode`` -- boolean, if ``True``, will produce LaTeX expressions
809
for labels;
810
811
- ``index_set`` -- a list of integers (default: ``range(n)``) that will be
812
used as subscripts for labels.
813
814
OUTPUT:
815
816
- a list of ``n`` labels.
817
818
If ``label`` was a list of ``n`` entries, it is returned without changes.
819
If ``label`` is ``None``, a list of ``n`` ``None``'s is returned. If
820
``label`` is a string, a list of strings of the form "$label_{i}$" is
821
returned, where `i` ranges over ``index_set``. (If ``math_mode=False``, the
822
form "label_i" is used instead.) If ``n=1``, there is no subscript added,
823
unless ``index_set`` was specified explicitly.
824
825
EXAMPLES::
826
827
sage: from sage.geometry.toric_plotter import label_list
828
sage: label_list("u", 3, False)
829
['u_0', 'u_1', 'u_2']
830
sage: label_list("u", 3, True)
831
['$u_{0}$', '$u_{1}$', '$u_{2}$']
832
sage: label_list("u", 1, True)
833
['$u$']
834
"""
835
if label is None:
836
return [None] * n
837
if isinstance(label, (list, tuple)):
838
if len(label) != n:
839
raise ValueError("expected %d labels, got %d!" % (n, len(label)))
840
return label
841
if index_set is None:
842
if n == 1:
843
return ["$%s$" % label.strip("$")] if math_mode else [label]
844
index_set = range(n)
845
if math_mode:
846
label = label.strip("$")
847
return list("$%s_{%d}$" % (label, i) for i in index_set)
848
else:
849
return list("%s_%d" % (label, i) for i in index_set)
850
851
852
def options(option=None, **kwds):
853
r"""
854
Get or set options for plots of toric geometry objects.
855
856
.. NOTE::
857
858
This function provides access to global default options. Any of these
859
options can be overridden by passing them directly to plotting
860
functions. See also :func:`reset_options`.
861
862
INPUT:
863
864
- None;
865
866
OR:
867
868
- ``option`` -- a string, name of the option whose value you wish to get;
869
870
OR:
871
872
- keyword arguments specifying new values for one or more options.
873
874
OUTPUT:
875
876
- if there was no input, the dictionary of current options for toric plots;
877
878
- if ``option`` argument was given, the current value of ``option``;
879
880
- if other keyword arguments were given, none.
881
882
**Name Conventions**
883
884
To clearly distinguish parts of toric plots, in options and methods we use
885
the following name conventions:
886
887
Generator
888
A primitive integral vector generating a 1-dimensional cone, plotted as
889
an arrow from the origin (or a line, if the head of the arrow is beyond
890
cut-off bounds for the plot).
891
892
Ray
893
A 1-dimensional cone, plotted as a line from the origin to the cut-off
894
bounds for the plot.
895
896
Wall
897
A 2-dimensional cone, plotted as a region between rays (in the above
898
sense). Its exact shape depends on the plotting mode (see below).
899
900
Chamber
901
A 3-dimensional cone, plotting is not implemented yet.
902
903
**Plotting Modes**
904
905
A plotting mode mostly determines the shape of the cut-off region (which is
906
always relevant for toric plots except for trivial objects consisting of
907
the origin only). The following options are available:
908
909
Box
910
The cut-off region is a box with edges parallel to coordinate axes.
911
912
Generators
913
The cut-off region is determined by primitive integral generators of
914
rays. Note that this notion is well-defined only for rays and walls,
915
in particular you should plot the lattice on your own
916
(:meth:`~ToricPlotter.plot_lattice` will use box mode which is likely
917
to be unsuitable). While this method may not be suitable for general
918
fans, it is quite natural for fans of :class:`CPR-Fano toric varieties.
919
<sage.schemes.toric.fano_variety.CPRFanoToricVariety_field`
920
921
Round
922
The cut-off regions is a sphere centered at the origin.
923
924
**Available Options**
925
926
Default values for the following options can be set using this function:
927
928
929
- ``mode`` -- "box", "generators", or "round", see above for descriptions;
930
931
- ``show_lattice`` -- boolean, whether to show lattice points in the
932
cut-off region or not;
933
934
- ``show_rays`` -- boolean, whether to show rays or not;
935
936
- ``show_generators`` -- boolean, whether to show rays or not;
937
938
- ``show_walls`` -- boolean, whether to show rays or not;
939
940
- ``generator_color`` -- a color for generators;
941
942
- ``label_color`` -- a color for labels;
943
944
- ``point_color`` -- a color for lattice points;
945
946
- ``ray_color`` -- a color for rays, a list of colors (one for each ray),
947
or the string "rainbow";
948
949
- ``wall_color`` -- a color for walls, a list of colors (one for each
950
wall), or the string "rainbow";
951
952
- ``wall_alpha`` -- a number between 0 and 1, the alpha-value for walls
953
(determining their transparency);
954
955
- ``point_size`` -- an integer, the size of lattice points;
956
957
- ``ray_thickness`` -- an integer, the thickness of rays;
958
959
- ``generator_thickness`` -- an integer, the thickness of generators;
960
961
- ``font_size`` -- an integer, the size of font used for labels;
962
963
- ``ray_label`` -- a string or a list of strings used for ray labels;
964
965
- ``wall_label`` -- a string or a list of strings used for wall labels;
966
967
- ``radius`` -- a positive number, the radius of the cut-off region for
968
"round" mode;
969
970
- ``xmin``, ``xmax``, ``ymin``, ``ymax``, ``zmin``, ``zmax`` -- numbers
971
determining the cut-off region for "box" mode. Note that you cannot
972
exclude the origin - if you try to do so, bounds will be automatically
973
expanded to include it;
974
975
- ``lattice_filter`` -- a callable, taking as an argument a lattice point
976
and returning ``True`` if this point should be included on the plot
977
(useful, e.g. for plotting sublattices);
978
979
- ``wall_zorder``, ``ray_zorder``, ``generator_zorder``, ``point_zorder``,
980
``label_zorder`` -- integers, z-orders for different classes of objects.
981
By default all values are negative, so that you can add other graphic
982
objects on top of a toric plot. You may need to adjust these parameters
983
if you want to put a toric plot on top of something else or if you want
984
to overlap several toric plots.
985
986
You can see the current default value of any options by typing, e.g. ::
987
988
sage: toric_plotter.options("show_rays")
989
True
990
991
If the default value is ``None``, it means that the actual default is
992
determined later based on the known options. Note, that not all options can
993
be determined in such a way, so you should not set options to ``None``
994
unless it was its original state. (You can always revert to this "original
995
state" using :meth:`reset_options`.)
996
997
EXAMPLES:
998
999
The following line will make all subsequent toric plotting commands to draw
1000
"rainbows" from walls::
1001
1002
sage: toric_plotter.options(wall_color="rainbow")
1003
1004
If you prefer a less colorful output (e.g. if you need black-and-white
1005
illustrations for a paper), you can use something like this::
1006
1007
sage: toric_plotter.options(wall_color="grey")
1008
"""
1009
global _options
1010
if option is None and not kwds:
1011
return copy(_options)
1012
elif option is not None and not kwds:
1013
try:
1014
return _options[option]
1015
except KeyError:
1016
_unrecognized_option(option)
1017
elif option is None and kwds:
1018
for option in kwds:
1019
try:
1020
_options[option] = kwds[option]
1021
except KeyError:
1022
_unrecognized_option(option)
1023
else:
1024
raise ValueError("you cannot specify 'option' and other arguments at "
1025
"the same time!")
1026
1027
1028
def reset_options():
1029
r"""
1030
Reset options for plots of toric geometry objects.
1031
1032
OUTPUT:
1033
1034
- none.
1035
1036
EXAMPLES::
1037
1038
sage: toric_plotter.options("show_rays")
1039
True
1040
sage: toric_plotter.options(show_rays=False)
1041
sage: toric_plotter.options("show_rays")
1042
False
1043
1044
Now all toric plots will not show rays, unless explicitly requested. If you
1045
want to go back to "default defaults", use this method::
1046
1047
sage: toric_plotter.reset_options()
1048
sage: toric_plotter.options("show_rays")
1049
True
1050
"""
1051
global _options
1052
_options = copy(_default_options)
1053
1054
1055
def sector(ray1, ray2, **extra_options):
1056
r"""
1057
Plot a sector between ``ray1`` and ``ray2`` centered at the origin.
1058
1059
.. NOTE::
1060
1061
This function was intended for plotting strictly convex cones, so it
1062
plots the smaller sector between ``ray1`` and ``ray2`` and, therefore,
1063
they cannot be opposite. If you do want to use this function for bigger
1064
regions, split them into several parts.
1065
1066
.. NOTE::
1067
1068
As of version 4.6 Sage does not have a graphic primitive for sectors in
1069
3-dimensional space, so this function will actually approximate them
1070
using polygons (the number of vertices used depends on the angle
1071
between rays).
1072
1073
INPUT:
1074
1075
- ``ray1``, ``ray2`` -- rays in 2- or 3-dimensional space of the same
1076
length;
1077
1078
- ``extra_options`` -- a dictionary of options that should be passed to
1079
lower level plotting functions.
1080
1081
OUTPUT:
1082
1083
- a plot.
1084
1085
EXAMPLES::
1086
1087
sage: from sage.geometry.toric_plotter import sector
1088
sage: print sector((1,0), (0,1))
1089
Graphics object consisting of 1 graphics primitive
1090
sage: print sector((3,2,1), (1,2,3))
1091
Graphics3d Object
1092
"""
1093
ray1 = vector(RDF, ray1)
1094
ray2 = vector(RDF, ray2)
1095
r = ray1.norm()
1096
if len(ray1) == 2:
1097
# Plot an honest sector
1098
phi1 = arctan2(ray1[1], ray1[0])
1099
phi2 = arctan2(ray2[1], ray2[0])
1100
if phi1 > phi2:
1101
phi1, phi2 = phi2, phi1
1102
if phi2 - phi1 > pi:
1103
phi1, phi2 = phi2, phi1 + 2 * pi
1104
return disk((0,0), r, (phi1, phi2), **extra_options)
1105
else:
1106
# Plot a polygon, 30 vertices per radian.
1107
vertices_per_radian = 30
1108
n = ceil(arccos(ray1 * ray2 / r**2) * vertices_per_radian)
1109
dr = (ray2 - ray1) / n
1110
points = (ray1 + i * dr for i in range(n + 1))
1111
points = [r / pt.norm() * pt for pt in points]
1112
points.append(vector(RDF, 3))
1113
return polygon(points, **extra_options)
1114
1115