Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/geometry/polyhedron/plot.py
4057 views
1
"""
2
Functions for plotting polyhedra
3
"""
4
5
########################################################################
6
# Copyright (C) 2008 Marshall Hampton <[email protected]>
7
# Copyright (C) 2011 Volker Braun <[email protected]>
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
#
11
# http://www.gnu.org/licenses/
12
########################################################################
13
14
15
from sage.rings.all import QQ, ZZ, RDF
16
from sage.structure.sage_object import SageObject
17
from sage.modules.free_module_element import vector
18
from sage.matrix.constructor import matrix, identity_matrix
19
from sage.misc.functional import norm
20
from sage.structure.sequence import Sequence
21
22
from sage.plot.all import point2d, line2d, arrow, polygon2d
23
from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d
24
from sage.graphs.graph import Graph
25
26
from base import is_Polyhedron
27
from constructor import Polyhedron
28
29
30
31
#############################################################
32
def render_2d(projection, **kwds):
33
"""
34
Return 2d rendering of the projection of a polyhedron into
35
2-dimensional ambient space.
36
37
EXAMPLES::
38
39
sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]])
40
sage: q1 = p1.projection()
41
sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]])
42
sage: q2 = p2.projection()
43
sage: p3 = Polyhedron(vertices=[[1,2]])
44
sage: q3 = p3.projection()
45
sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]])
46
sage: q4 = p4.projection()
47
sage: q1.show() + q2.show() + q3.show() + q4.show()
48
sage: from sage.geometry.polyhedron.plot import render_2d
49
sage: q = render_2d(p1.projection())
50
sage: q._Graphics__objects
51
[Point set defined by 1 point(s),
52
Arrow from (1.0,1.0) to (2.0,2.0),
53
Polygon defined by 3 points]
54
"""
55
if is_Polyhedron(projection):
56
projection = Projection(projection)
57
return \
58
projection.render_points_2d(zorder=2, pointsize=10, **kwds) + \
59
projection.render_outline_2d(zorder=1, **kwds) + \
60
projection.render_fill_2d(zorder=0, rgbcolor=(0,1,0), **kwds)
61
62
63
def render_3d(projection, **kwds):
64
"""
65
Return 3d rendering of a polyhedron projected into
66
3-dimensional ambient space.
67
68
NOTE:
69
70
This method, ``render_3d``, is used in the ``show()``
71
method of a polyhedron if it is in 3 dimensions.
72
73
EXAMPLES::
74
75
sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]])
76
sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]])
77
sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]], rays=[[-1,-1,-1]])
78
sage: p1.projection().show() + p2.projection().show() + p3.projection().show()
79
80
It correctly handles various degenerate cases::
81
82
sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).show() # whole space
83
sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]]).show() # half space
84
sage: Polyhedron(vertices=[[1,1,1]], lines=[[0,1,0],[0,0,1]]).show() # R^2 in R^3
85
sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).show() # quadrant wedge in R^2
86
sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).show() # upper half plane in R^3
87
sage: Polyhedron(lines=[[1,0,0]]).show() # R^1 in R^2
88
sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^3
89
sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^3
90
"""
91
if is_Polyhedron(projection):
92
projection = Projection(projection)
93
return \
94
projection.render_vertices_3d(width=3, color='green', **kwds) +\
95
projection.render_wireframe_3d(width=3, color='green', **kwds) + \
96
projection.render_solid_3d(**kwds)
97
98
99
def render_4d(polyhedron, **kwds):
100
"""
101
Return a 3d rendering of the Schlegel projection of a 4d
102
polyhedron projected into 3-dimensional space.
103
104
NOTES:
105
106
The ``show()`` method of ``Polyhedron()`` uses this to draw itself
107
if the ambient dimension is 4.
108
109
INPUT:
110
111
- ``polyhedron`` -- A
112
:mod:`~sage.geometry.polyhedron.constructor.Polyhedron` object.
113
114
- ``kwds`` -- plot keywords. Passing
115
``projection_direction=<list>`` sets the projetion direction of
116
the Schlegel projection. If it is not given, the center of a
117
facet is used.
118
119
EXAMPLES::
120
121
sage: poly = polytopes.twenty_four_cell()
122
sage: poly
123
A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 24 vertices
124
sage: poly.show()
125
sage: poly.show(projection_direction=[2,5,11,17])
126
sage: type( poly.show() )
127
<class 'sage.plot.plot3d.base.Graphics3dGroup'>
128
129
TESTS::
130
131
sage: from sage.geometry.polyhedron.plot import render_4d
132
sage: p = polytopes.n_cube(4)
133
sage: q = render_4d(p)
134
sage: tach_str = q.tachyon()
135
sage: tach_str.count('FCylinder')
136
32
137
"""
138
projection_direction = None
139
try:
140
projection_direction = kwds.pop('projection_direction')
141
except KeyError:
142
for ineq in polyhedron.inequality_generator():
143
center = [v() for v in ineq.incident() if v.is_vertex()]
144
center = sum(center) / len(center)
145
if not center.is_zero():
146
projection_direction = center
147
break
148
projection_3d = Projection(polyhedron).schlegel(projection_direction)
149
return render_3d(projection_3d, **kwds)
150
151
152
153
#############################################################
154
def cyclic_sort_vertices_2d(Vlist):
155
"""
156
Return the vertices/rays in cyclic order if possible.
157
158
NOTES:
159
160
This works if and only if each vertex/ray is adjacent to exactly
161
two others. For example, any 2-dimensional polyhedron satisfies
162
this.
163
164
EXAMPLES::
165
166
sage: from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d
167
sage: square = Polyhedron([[1,0],[-1,0],[0,1],[0,-1]])
168
sage: vertices = [v for v in square.vertex_generator()]
169
sage: vertices
170
[A vertex at (-1, 0),
171
A vertex at (0, -1),
172
A vertex at (0, 1),
173
A vertex at (1, 0)]
174
sage: cyclic_sort_vertices_2d(vertices)
175
[A vertex at (1, 0),
176
A vertex at (0, -1),
177
A vertex at (-1, 0),
178
A vertex at (0, 1)]
179
"""
180
if len(Vlist)==0: return Vlist
181
182
adjacency_matrix = Vlist[0].polyhedron().vertex_adjacency_matrix()
183
result = [ Vlist.pop() ]
184
while len(Vlist)>0:
185
for i in range(len(Vlist)):
186
if adjacency_matrix[Vlist[i].index(), result[-1].index()] == 1:
187
result.append( Vlist.pop(i) )
188
break;
189
else:
190
raise ValueError
191
return result
192
193
194
195
196
#########################################################################
197
def projection_func_identity(x):
198
"""
199
The identity projection.
200
201
EXAMPLES::
202
203
sage: from sage.geometry.polyhedron.plot import projection_func_identity
204
sage: projection_func_identity((1,2,3))
205
[1, 2, 3]
206
"""
207
return list(x)
208
209
210
211
class ProjectionFuncStereographic():
212
"""
213
The stereographic (or perspective) projection.
214
215
EXAMPLES::
216
217
sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
218
sage: cube = polytopes.n_cube(3).vertices()
219
sage: proj = ProjectionFuncStereographic([1.2, 3.4, 5.6])
220
sage: ppoints = [proj(vector(x)) for x in cube]
221
sage: ppoints[0]
222
(-0.0486511..., 0.0859565...)
223
"""
224
def __init__(self, projection_point):
225
"""
226
Create a stereographic projection function.
227
228
INPUT:
229
230
- ``projection_point`` -- a list of coordinates in the
231
appropriate dimension, which is the point projected from.
232
233
EXAMPLES::
234
235
sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
236
sage: proj = ProjectionFuncStereographic([1.0,1.0])
237
sage: proj.__init__([1.0,1.0])
238
sage: proj.house
239
[-0.7071067811... 0.7071067811...]
240
[ 0.7071067811... 0.7071067811...]
241
sage: TestSuite(proj).run(skip='_test_pickling')
242
"""
243
self.projection_point = vector(projection_point)
244
self.dim = self.projection_point.degree()
245
246
pproj = vector(RDF,self.projection_point)
247
self.psize = norm(pproj)
248
if (self.psize).is_zero():
249
raise ValueError, "projection direction must be a non-zero vector."
250
v = vector(RDF, [0.0]*(self.dim-1) + [self.psize]) - pproj
251
polediff = matrix(RDF,v).transpose()
252
denom = RDF((polediff.transpose()*polediff)[0][0])
253
if denom.is_zero():
254
self.house = identity_matrix(RDF,self.dim)
255
else:
256
self.house = identity_matrix(RDF,self.dim) \
257
- 2*polediff*polediff.transpose()/denom # Householder reflector
258
259
def __call__(self, x):
260
"""
261
Action of the stereographic projection.
262
263
INPUT:
264
265
- ``x`` -- a vector or anything convertible to a vector.
266
267
OUTPUT:
268
269
First reflects ``x`` with a Householder reflection which takes
270
the projection point to ``(0,...,0,self.psize)`` where
271
``psize`` is the length of the projection point, and then
272
dilates by ``1/(zdiff)`` where ``zdiff`` is the difference
273
between the last coordinate of ``x`` and ``psize``.
274
275
EXAMPLES::
276
277
sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
278
sage: proj = ProjectionFuncStereographic([1.0,1.0])
279
sage: proj.__call__(vector([1,2]))
280
(-1.0)
281
sage: proj = ProjectionFuncStereographic([2.0,1.0])
282
sage: proj.__call__(vector([1,2]))
283
(3.0)
284
sage: proj = ProjectionFuncStereographic([0,0,2])
285
sage: proj.__call__(vector([0,0,1]))
286
(0.0, 0.0)
287
sage: proj.__call__(vector([1,0,0]))
288
(0.5, 0.0)
289
"""
290
img = self.house * x
291
denom = self.psize-img[self.dim-1]
292
if denom.is_zero():
293
raise ValueError, 'Point cannot coincide with ' \
294
'coordinate singularity at ' + repr(x)
295
return vector(RDF, [img[i]/denom for i in range(self.dim-1)])
296
297
298
class ProjectionFuncSchlegel():
299
"""
300
The Schlegel projection from the given input point.
301
302
EXAMPLES::
303
304
sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
305
sage: proj = ProjectionFuncSchlegel([2,2,2])
306
sage: proj(vector([1.1,1.1,1.11]))[0]
307
0.0302...
308
"""
309
def __init__(self, projection_direction, height = 1.1):
310
"""
311
Initializes the projection.
312
313
EXAMPLES::
314
315
sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
316
sage: proj = ProjectionFuncSchlegel([2,2,2])
317
sage: proj.__init__([2,2,2])
318
sage: proj(vector([1.1,1.1,1.11]))[0]
319
0.0302...
320
sage: TestSuite(proj).run(skip='_test_pickling')
321
"""
322
self.projection_dir = vector(RDF, projection_direction)
323
if norm(self.projection_dir).is_zero():
324
raise ValueError, "projection direction must be a non-zero vector."
325
self.dim = self.projection_dir.degree()
326
spcenter = height * self.projection_dir/norm(self.projection_dir)
327
self.height = height
328
v = vector(RDF, [0.0]*(self.dim-1) + [self.height]) - spcenter
329
polediff = matrix(RDF,v).transpose()
330
denom = (polediff.transpose()*polediff)[0][0]
331
if denom.is_zero():
332
self.house = identity_matrix(RDF,self.dim)
333
else:
334
self.house = identity_matrix(RDF,self.dim) \
335
- 2*polediff*polediff.transpose()/denom #Householder reflector
336
337
def __call__(self, x):
338
"""
339
Apply the projection to a vector.
340
341
- ``x`` -- a vector or anything convertible to a vector.
342
343
EXAMPLES::
344
345
sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
346
sage: proj = ProjectionFuncSchlegel([2,2,2])
347
sage: proj.__call__([1,2,3])
348
(0.56162854..., 2.09602626...)
349
"""
350
v = vector(RDF,x)
351
if v.is_zero():
352
raise ValueError, "The origin must not be a vertex."
353
v = v/norm(v) # normalize vertices to unit sphere
354
v = self.house*v # reflect so self.projection_dir is at "north pole"
355
denom = self.height-v[self.dim-1]
356
if denom.is_zero():
357
raise ValueError, 'Point cannot coincide with ' \
358
'coordinate singularity at ' + repr(x)
359
return vector(RDF, [ v[i]/denom for i in range(self.dim-1) ])
360
361
362
363
#########################################################################
364
class Projection(SageObject):
365
"""
366
The projection of a :class:`Polyhedron`.
367
368
This class keeps track of the necessary data to plot the input
369
polyhedron.
370
"""
371
372
def __init__(self, polyhedron, proj=projection_func_identity):
373
"""
374
Initialize the projection of a Polyhedron() object.
375
376
INPUT:
377
378
- ``polyhedron`` -- a ``Polyhedron()`` object
379
380
- ``proj`` -- a projection function for the points
381
382
NOTES:
383
384
Once initialized, the polyhedral data is fixed. However, the
385
projection can be changed later on.
386
387
EXAMPLES::
388
389
sage: p = polytopes.icosahedron()
390
sage: from sage.geometry.polyhedron.plot import Projection
391
sage: Projection(p)
392
The projection of a polyhedron into 3 dimensions
393
sage: def pr_12(x): return [x[1],x[2]]
394
sage: Projection(p, pr_12)
395
The projection of a polyhedron into 2 dimensions
396
sage: Projection(p, lambda x: [x[1],x[2]] ) # another way of doing the same projection
397
The projection of a polyhedron into 2 dimensions
398
sage: _.show() # plot of the projected icosahedron in 2d
399
sage: proj = Projection(p)
400
sage: proj.stereographic([1,2,3])
401
The projection of a polyhedron into 2 dimensions
402
sage: proj.show()
403
sage: TestSuite(proj).run(skip='_test_pickling')
404
"""
405
self.coords = Sequence([])
406
self.points = Sequence([])
407
self.lines = Sequence([])
408
self.arrows = Sequence([])
409
self.polygons = Sequence([])
410
self.polyhedron_ambient_dim = polyhedron.ambient_dim()
411
412
if polyhedron.ambient_dim() == 2:
413
self._init_from_2d(polyhedron)
414
elif polyhedron.ambient_dim() == 3:
415
self._init_from_3d(polyhedron)
416
else:
417
self._init_points(polyhedron)
418
self._init_lines_arrows(polyhedron)
419
420
self.coords.set_immutable()
421
self.points.set_immutable()
422
self.lines.set_immutable()
423
self.arrows.set_immutable()
424
self.polygons.set_immutable()
425
426
self.__call__(proj)
427
428
429
def _repr_(self):
430
"""
431
Return a string describing the projection.
432
433
EXAMPLES::
434
435
sage: p = polytopes.n_cube(3)
436
sage: from sage.geometry.polyhedron.plot import Projection
437
sage: proj = Projection(p)
438
sage: print proj._repr_()
439
The projection of a polyhedron into 3 dimensions
440
"""
441
s = 'The projection of a polyhedron into ' + \
442
repr(self.dimension) + ' dimensions'
443
return s
444
445
446
def __call__(self, proj=projection_func_identity):
447
"""
448
Apply a projection.
449
450
EXAMPLES::
451
452
sage: p = polytopes.icosahedron()
453
sage: from sage.geometry.polyhedron.plot import Projection
454
sage: pproj = Projection(p)
455
sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
456
sage: pproj_stereo = pproj.__call__(proj = ProjectionFuncStereographic([1,2,3]))
457
sage: pproj_stereo.polygons[0]
458
[10, 4, 6]
459
"""
460
self.transformed_coords = \
461
Sequence([proj(p) for p in self.coords])
462
self._init_dimension()
463
return self
464
465
466
def identity(self):
467
"""
468
Return the identity projection of the polyhedron.
469
470
EXAMPLES::
471
472
sage: p = polytopes.icosahedron()
473
sage: from sage.geometry.polyhedron.plot import Projection
474
sage: pproj = Projection(p)
475
sage: ppid = pproj.identity()
476
sage: ppid.dimension
477
3
478
"""
479
return self.__call__(projection_func_identity)
480
481
482
def stereographic(self, projection_point=None):
483
r"""
484
Rteurn the stereographic projection.
485
486
INPUT:
487
488
- ``projection_point`` - The projection point. This must be
489
distinct from the polyhedron's vertices. Default is `(1,0,\dots,0)`
490
491
EXAMPLES::
492
493
sage: from sage.geometry.polyhedron.plot import Projection
494
sage: proj = Projection(polytopes.buckyball()) #long time
495
sage: proj #long time
496
The projection of a polyhedron into 3 dimensions
497
sage: proj.stereographic([5,2,3]).show() #long time
498
sage: Projection( polytopes.twenty_four_cell() ).stereographic([2,0,0,0])
499
The projection of a polyhedron into 3 dimensions
500
"""
501
if projection_point == None:
502
projection_point = [1] + [0]*(self.polyhedron_ambient_dim-1)
503
return self.__call__(ProjectionFuncStereographic(projection_point))
504
505
506
def schlegel(self, projection_direction=None, height = 1.1):
507
"""
508
Return the Schlegel projection.
509
510
The vertices are normalized to the unit sphere, and
511
stereographically projected from a point slightly outside of
512
the sphere.
513
514
INPUT:
515
516
- ``projection_direction`` - The direction of the Schlegel
517
projection. By default, the vector consisting of the first
518
n primes is chosen.
519
520
EXAMPLES::
521
522
sage: cube4 = polytopes.n_cube(4)
523
sage: from sage.geometry.polyhedron.plot import Projection
524
sage: Projection(cube4).schlegel([1,0,0,0])
525
The projection of a polyhedron into 3 dimensions
526
sage: _.show()
527
"""
528
if projection_direction == None:
529
for poly in self.polygons:
530
center = sum([self.coords[i] for i in poly]) / len(poly)
531
print center, "\n"
532
if not center.is_zero():
533
projection_direction = center
534
break
535
if projection_direction == None:
536
projection_direction = primes_first_n(self.polyhedron_ambient_dim)
537
return self.__call__(ProjectionFuncSchlegel(projection_direction, height = height))
538
539
540
def coord_index_of(self, v):
541
"""
542
Convert a coordinate vector to its internal index.
543
544
EXAMPLES::
545
546
sage: p = polytopes.n_cube(3)
547
sage: proj = p.projection()
548
sage: proj.coord_index_of(vector((1,1,1)))
549
7
550
"""
551
try:
552
return self.coords.index(v)
553
except ValueError:
554
self.coords.append(v)
555
return len(self.coords)-1
556
557
558
def coord_indices_of(self, v_list):
559
"""
560
Convert list of coordinate vectors to the corresponding list
561
of internal indices.
562
563
EXAMPLES::
564
565
sage: p = polytopes.n_cube(3)
566
sage: proj = p.projection()
567
sage: proj.coord_indices_of([vector((1,1,1)),vector((1,-1,1))])
568
[7, 5]
569
"""
570
return [self.coord_index_of(v) for v in v_list]
571
572
573
def coordinates_of(self, coord_index_list):
574
"""
575
Given a list of indices, return the projected coordinates.
576
577
EXAMPLES::
578
579
sage: p = polytopes.n_simplex(4).projection()
580
sage: p.coordinates_of([1])
581
[[0, -81649/100000, 7217/25000, 22361/100000]]
582
"""
583
return [self.transformed_coords[i] for i in coord_index_list]
584
585
586
def _init_dimension(self):
587
"""
588
Internal function: Initialize from 2d polyhedron. Must always
589
be called after a coordinate projection.
590
591
TESTS::
592
593
sage: from sage.geometry.polyhedron.plot import Projection, render_2d
594
sage: p = polytopes.n_simplex(2).projection()
595
sage: test = p._init_dimension()
596
sage: p.show.__doc__ == render_2d.__doc__
597
True
598
"""
599
self.dimension = len(self.transformed_coords[0])
600
601
if self.dimension == 2:
602
self.show = lambda **kwds: render_2d(self,**kwds)
603
self.show.__doc__ = render_2d.__doc__
604
elif self.dimension == 3:
605
self.show = lambda **kwds: render_3d(self,**kwds)
606
self.show.__doc__ = render_3d.__doc__
607
else:
608
try:
609
del self.show
610
except AttributeError:
611
pass
612
613
614
def _init_from_2d(self, polyhedron):
615
"""
616
Internal function: Initialize from 2d polyhedron.
617
618
TESTS::
619
620
sage: p = Polyhedron(vertices = [[0,0],[0,1],[1,0],[1,1]])
621
sage: proj = p.projection()
622
sage: [proj.coordinates_of([i]) for i in proj.points]
623
[[[0, 0]], [[0, 1]], [[1, 0]], [[1, 1]]]
624
sage: proj._init_from_2d
625
<bound method Projection._init_from_2d of The projection
626
of a polyhedron into 2 dimensions>
627
"""
628
assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"
629
self.dimension = 2
630
self._init_points(polyhedron)
631
self._init_lines_arrows(polyhedron)
632
self._init_area_2d(polyhedron)
633
634
635
def _init_from_3d(self, polyhedron):
636
"""
637
Internal function: Initialize from 3d polyhedron.
638
639
TESTS::
640
641
sage: p = Polyhedron(vertices = [[0,0,1],[0,1,2],[1,0,3],[1,1,5]])
642
sage: proj = p.projection()
643
sage: [proj.coordinates_of([i]) for i in proj.points]
644
[[[0, 0, 1]], [[0, 1, 2]], [[1, 0, 3]], [[1, 1, 5]]]
645
sage: proj._init_from_3d
646
<bound method Projection._init_from_3d of The projection
647
of a polyhedron into 3 dimensions>
648
"""
649
assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"
650
self.dimension = 3
651
self._init_points(polyhedron)
652
self._init_lines_arrows(polyhedron)
653
self._init_solid_3d(polyhedron)
654
655
656
def _init_points(self, polyhedron):
657
"""
658
Internal function: Initialize points (works in arbitrary
659
dimensions).
660
661
TESTS::
662
663
sage: p = polytopes.n_cube(2)
664
sage: pp = p.projection()
665
sage: del pp.points
666
sage: pp.points = Sequence([])
667
sage: pp._init_points(p)
668
sage: pp.points
669
[0, 1, 2, 3]
670
"""
671
for v in polyhedron.vertex_generator():
672
self.points.append( self.coord_index_of(v.vector()) )
673
674
675
def _init_lines_arrows(self, polyhedron):
676
"""
677
Internal function: Initialize compact and non-compact edges
678
(works in arbitrary dimensions).
679
680
TESTS::
681
682
sage: p = Polyhedron(ieqs = [[1, 0, 0, 1],[1,1,0,0]])
683
sage: pp = p.projection()
684
sage: pp.arrows
685
[[0, 1], [0, 2], [0, 3], [0, 4]]
686
sage: del pp.arrows
687
sage: pp.arrows = Sequence([])
688
sage: pp._init_lines_arrows(p)
689
sage: pp.arrows
690
[[0, 1], [0, 2], [0, 3], [0, 4]]
691
"""
692
obj = polyhedron.Vrepresentation()
693
for i in range(len(obj)):
694
if not obj[i].is_vertex(): continue
695
for j in range(len(obj)):
696
if polyhedron.vertex_adjacency_matrix()[i,j] == 0: continue
697
if i<j and obj[j].is_vertex():
698
l = [obj[i].vector(), obj[j].vector()]
699
self.lines.append( [ self.coord_index_of(l[0]),
700
self.coord_index_of(l[1]) ] )
701
if obj[j].is_ray():
702
l = [obj[i].vector(), obj[i].vector() + obj[j].vector()]
703
self.arrows.append( [ self.coord_index_of(l[0]),
704
self.coord_index_of(l[1]) ] )
705
if obj[j].is_line():
706
l1 = [obj[i].vector(), obj[i].vector() + obj[j].vector()]
707
l2 = [obj[i].vector(), obj[i].vector() - obj[j].vector()]
708
self.arrows.append( [ self.coord_index_of(l1[0]),
709
self.coord_index_of(l1[1]) ] )
710
self.arrows.append( [ self.coord_index_of(l2[0]),
711
self.coord_index_of(l2[1]) ] )
712
713
714
def _init_area_2d(self, polyhedron):
715
"""
716
Internal function: Initialize polygon area for 2d polyhedron.
717
718
TESTS::
719
720
sage: p = polytopes.cyclic_polytope(2,4)
721
sage: proj = p.projection()
722
sage: proj.polygons = Sequence([])
723
sage: proj._init_area_2d(p)
724
sage: proj.polygons
725
[[3, 0, 1, 2]]
726
"""
727
assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"
728
vertices = [v for v in polyhedron.Vrep_generator()]
729
vertices = cyclic_sort_vertices_2d(vertices)
730
coords = []
731
732
def adjacent_vertices(i):
733
n = len(vertices)
734
if vertices[(i-1) % n].is_vertex(): yield vertices[(i-1) % n]
735
if vertices[(i+1) % n].is_vertex(): yield vertices[(i+1) % n]
736
737
for i in range(len(vertices)):
738
v = vertices[i]
739
if v.is_vertex():
740
coords.append(v())
741
if v.is_ray():
742
for a in adjacent_vertices(i):
743
coords.append(a() + v())
744
745
if polyhedron.n_lines() == 0:
746
self.polygons.append( self.coord_indices_of(coords) )
747
return
748
749
polygons = []
750
751
if polyhedron.n_lines() == 1:
752
aline = polyhedron.line_generator().next()
753
for shift in [aline(), -aline()]:
754
for i in range(len(coords)):
755
polygons.append( [ coords[i-1],coords[i],
756
coords[i]+shift, coords[i-1]+shift ] )
757
758
if polyhedron.n_lines() == 2:
759
[line1, line2] = [l for l in polyhedron.lines()]
760
assert len(coords)==1, "Can have only a single vertex!"
761
v = coords[0]
762
l1 = line1()
763
l2 = line2()
764
polygons = [ [v-l1-l2, v+l1-l2, v+l1+l2, v-l1+l2] ]
765
766
polygons = [ self.coord_indices_of(p) for p in polygons ]
767
self.polygons.extend(polygons)
768
769
770
771
def _init_solid_3d(self, polyhedron):
772
"""
773
Internal function: Initialize facet polygons for 3d polyhedron.
774
775
TESTS::
776
777
sage: p = polytopes.cyclic_polytope(3,4)
778
sage: proj = p.projection()
779
sage: proj.polygons = Sequence([])
780
sage: proj._init_solid_3d(p)
781
sage: proj.polygons
782
[[2, 0, 1], [3, 0, 1], [3, 0, 2], [3, 1, 2]]
783
"""
784
assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"
785
786
if polyhedron.dim() <= 1: # empty or 0d or 1d polyhedron => no polygon
787
return None
788
789
def defining_equation(): # corresponding to a polygon
790
if polyhedron.dim() < 3:
791
yield polyhedron.equation_generator().next()
792
else:
793
for ineq in polyhedron.inequality_generator():
794
yield ineq
795
796
faces = []
797
for facet_equation in defining_equation():
798
vertices = [v for v in facet_equation.incident()]
799
vertices = cyclic_sort_vertices_2d(vertices)
800
coords = []
801
802
def adjacent_vertices(i):
803
n = len(vertices)
804
if vertices[(i-1) % n].is_vertex(): yield vertices[(i-1) % n]
805
if vertices[(i+1) % n].is_vertex(): yield vertices[(i+1) % n]
806
807
for i in range(len(vertices)):
808
v = vertices[i]
809
if v.is_vertex():
810
coords.append(v())
811
if v.is_ray():
812
for a in adjacent_vertices(i):
813
coords.append(a() + v())
814
815
faces.append(coords)
816
817
if polyhedron.n_lines() == 0:
818
assert len(faces)>0, "no vertices?"
819
self.polygons.extend( [self.coord_indices_of(f) for f in faces] )
820
return
821
822
# now some special cases if there are lines (dim < ambient_dim)
823
polygons = []
824
825
if polyhedron.n_lines()==1:
826
assert len(faces)>0, "no vertices?"
827
aline = polyhedron.line_generator().next()
828
for shift in [aline(), -aline()]:
829
for coords in faces:
830
assert len(coords)==2, "There must be two points."
831
polygons.append( [ coords[0],coords[1],
832
coords[1]+shift, coords[0]+shift ] )
833
834
if polyhedron.n_lines()==2:
835
[line1, line2] = [l for l in polyhedron.line_generator()]
836
l1 = line1()
837
l2 = line2()
838
for v in polyhedron.vertex_generator():
839
polygons.append( [v()-l1-l2, v()+l1-l2, v()+l1+l2, v()-l1+l2] )
840
841
self.polygons.extend( [self.coord_indices_of(p) for p in polygons] )
842
843
844
def render_points_2d(self, **kwds):
845
"""
846
Return the points of a polyhedron in 2d.
847
848
EXAMPLES::
849
850
sage: hex = polytopes.regular_polygon(6)
851
sage: proj = hex.projection()
852
sage: hex_points = proj.render_points_2d()
853
sage: hex_points._Graphics__objects
854
[Point set defined by 6 point(s)]
855
"""
856
return point2d(self.coordinates_of(self.points), **kwds)
857
858
859
def render_outline_2d(self, **kwds):
860
"""
861
Return the outline (edges) of a polyhedron in 2d.
862
863
EXAMPLES::
864
865
sage: penta = polytopes.regular_polygon(5)
866
sage: outline = penta.projection().render_outline_2d()
867
sage: outline._Graphics__objects[0]
868
Line defined by 2 points
869
"""
870
wireframe = [];
871
for l in self.lines:
872
l_coords = self.coordinates_of(l)
873
wireframe.append( line2d(l_coords, **kwds) )
874
for a in self.arrows:
875
a_coords = self.coordinates_of(a)
876
wireframe.append( arrow(a_coords[0], a_coords[1], **kwds) )
877
return sum(wireframe)
878
879
880
def render_fill_2d(self, **kwds):
881
"""
882
Return the filled interior (a polygon) of a polyhedron in 2d.
883
884
EXAMPLES::
885
886
sage: cps = [i^3 for i in srange(-2,2,1/5)]
887
sage: p = Polyhedron(vertices = [[(t^2-1)/(t^2+1),2*t/(t^2+1)] for t in cps])
888
sage: proj = p.projection()
889
sage: filled_poly = proj.render_fill_2d()
890
sage: filled_poly.axes_width()
891
0.8
892
"""
893
poly = [polygon2d(self.coordinates_of(p), **kwds)
894
for p in self.polygons]
895
return sum(poly)
896
897
898
def render_vertices_3d(self, **kwds):
899
"""
900
Return the 3d rendering of the vertices.
901
902
EXAMPLES::
903
904
sage: p = polytopes.cross_polytope(3)
905
sage: proj = p.projection()
906
sage: verts = proj.render_vertices_3d()
907
sage: verts.bounding_box()
908
((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0))
909
"""
910
return point3d(self.coordinates_of(self.points), **kwds)
911
912
913
def render_wireframe_3d(self, **kwds):
914
r"""
915
Return the 3d wireframe rendering.
916
917
EXAMPLES::
918
919
sage: cube = polytopes.n_cube(3)
920
sage: cube_proj = cube.projection()
921
sage: wire = cube_proj.render_wireframe_3d()
922
sage: print wire.tachyon().split('\n')[77] # for testing
923
FCylinder base -1.0 1.0 -1.0 apex -1.0 -1.0 -1.0 rad 0.005 texture...
924
"""
925
wireframe = [];
926
for l in self.lines:
927
l_coords = self.coordinates_of(l)
928
wireframe.append( line3d(l_coords, **kwds))
929
for a in self.arrows:
930
a_coords = self.coordinates_of(a)
931
wireframe.append(arrow3d(a_coords[0], a_coords[1], **kwds))
932
return sum(wireframe)
933
934
935
def render_solid_3d(self, **kwds):
936
"""
937
Return solid 3d rendering of a 3d polytope.
938
939
EXAMPLES::
940
941
sage: p = polytopes.n_cube(3).projection()
942
sage: p_solid = p.render_solid_3d(opacity = .7)
943
sage: type(p_solid)
944
<class 'sage.plot.plot3d.base.Graphics3dGroup'>
945
"""
946
return sum([ polygon3d(self.coordinates_of(f), **kwds)
947
for f in self.polygons ])
948
949