passagemath-polyhedra 10.6.31rc3__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (206) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +206 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +6 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgmp-6e109695.so.10.5.0 +0 -0
  7. passagemath_polyhedra.libs/libgomp-e985bcbb.so.1.0.0 +0 -0
  8. sage/all__sagemath_polyhedra.py +50 -0
  9. sage/game_theory/all.py +8 -0
  10. sage/game_theory/catalog.py +6 -0
  11. sage/game_theory/catalog_normal_form_games.py +923 -0
  12. sage/game_theory/cooperative_game.py +844 -0
  13. sage/game_theory/matching_game.py +1181 -0
  14. sage/game_theory/normal_form_game.py +2697 -0
  15. sage/game_theory/parser.py +275 -0
  16. sage/geometry/all__sagemath_polyhedra.py +22 -0
  17. sage/geometry/cone.py +6940 -0
  18. sage/geometry/cone_catalog.py +847 -0
  19. sage/geometry/cone_critical_angles.py +1027 -0
  20. sage/geometry/convex_set.py +1119 -0
  21. sage/geometry/fan.py +3743 -0
  22. sage/geometry/fan_isomorphism.py +389 -0
  23. sage/geometry/fan_morphism.py +1884 -0
  24. sage/geometry/hasse_diagram.py +202 -0
  25. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  26. sage/geometry/hyperplane_arrangement/all.py +1 -0
  27. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  28. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  29. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  30. sage/geometry/hyperplane_arrangement/library.py +825 -0
  31. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  32. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  33. sage/geometry/integral_points.py +35 -0
  34. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-gnu.so +0 -0
  35. sage/geometry/integral_points_generic_dense.pyx +7 -0
  36. sage/geometry/lattice_polytope.py +5894 -0
  37. sage/geometry/linear_expression.py +773 -0
  38. sage/geometry/newton_polygon.py +767 -0
  39. sage/geometry/point_collection.cpython-314-x86_64-linux-gnu.so +0 -0
  40. sage/geometry/point_collection.pyx +1008 -0
  41. sage/geometry/polyhedral_complex.py +2616 -0
  42. sage/geometry/polyhedron/all.py +8 -0
  43. sage/geometry/polyhedron/backend_cdd.py +460 -0
  44. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  45. sage/geometry/polyhedron/backend_field.py +347 -0
  46. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  47. sage/geometry/polyhedron/backend_number_field.py +168 -0
  48. sage/geometry/polyhedron/backend_polymake.py +765 -0
  49. sage/geometry/polyhedron/backend_ppl.py +582 -0
  50. sage/geometry/polyhedron/base.py +1206 -0
  51. sage/geometry/polyhedron/base0.py +1444 -0
  52. sage/geometry/polyhedron/base1.py +886 -0
  53. sage/geometry/polyhedron/base2.py +812 -0
  54. sage/geometry/polyhedron/base3.py +1845 -0
  55. sage/geometry/polyhedron/base4.py +1262 -0
  56. sage/geometry/polyhedron/base5.py +2700 -0
  57. sage/geometry/polyhedron/base6.py +1741 -0
  58. sage/geometry/polyhedron/base7.py +997 -0
  59. sage/geometry/polyhedron/base_QQ.py +1258 -0
  60. sage/geometry/polyhedron/base_RDF.py +98 -0
  61. sage/geometry/polyhedron/base_ZZ.py +934 -0
  62. sage/geometry/polyhedron/base_mutable.py +215 -0
  63. sage/geometry/polyhedron/base_number_field.py +122 -0
  64. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  65. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  66. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-gnu.so +0 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-gnu.so +0 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-gnu.so +0 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-gnu.so +0 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-gnu.so +0 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-gnu.so +0 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-gnu.so +0 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  88. sage/geometry/polyhedron/constructor.py +773 -0
  89. sage/geometry/polyhedron/double_description.py +753 -0
  90. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  91. sage/geometry/polyhedron/face.py +1060 -0
  92. sage/geometry/polyhedron/generating_function.py +1810 -0
  93. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  94. sage/geometry/polyhedron/library.py +3502 -0
  95. sage/geometry/polyhedron/misc.py +121 -0
  96. sage/geometry/polyhedron/modules/all.py +1 -0
  97. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  98. sage/geometry/polyhedron/palp_database.py +447 -0
  99. sage/geometry/polyhedron/parent.py +1279 -0
  100. sage/geometry/polyhedron/plot.py +1986 -0
  101. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  102. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  103. sage/geometry/polyhedron/representation.py +1723 -0
  104. sage/geometry/pseudolines.py +515 -0
  105. sage/geometry/relative_interior.py +445 -0
  106. sage/geometry/toric_plotter.py +1103 -0
  107. sage/geometry/triangulation/all.py +2 -0
  108. sage/geometry/triangulation/base.cpython-314-x86_64-linux-gnu.so +0 -0
  109. sage/geometry/triangulation/base.pyx +963 -0
  110. sage/geometry/triangulation/data.h +147 -0
  111. sage/geometry/triangulation/data.pxd +4 -0
  112. sage/geometry/triangulation/element.py +914 -0
  113. sage/geometry/triangulation/functions.h +10 -0
  114. sage/geometry/triangulation/functions.pxd +4 -0
  115. sage/geometry/triangulation/point_configuration.py +2256 -0
  116. sage/geometry/triangulation/triangulations.h +49 -0
  117. sage/geometry/triangulation/triangulations.pxd +7 -0
  118. sage/geometry/voronoi_diagram.py +319 -0
  119. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  120. sage/interfaces/polymake.py +2028 -0
  121. sage/numerical/all.py +13 -0
  122. sage/numerical/all__sagemath_polyhedra.py +11 -0
  123. sage/numerical/backends/all.py +1 -0
  124. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  125. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  126. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  127. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  128. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  129. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  130. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  131. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  132. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  133. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  134. sage/numerical/backends/generic_backend_test.py +24 -0
  135. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  136. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  137. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  138. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  139. sage/numerical/backends/logging_backend.py +391 -0
  140. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  141. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  142. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  143. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  144. sage/numerical/backends/ppl_backend.pyx +1126 -0
  145. sage/numerical/backends/ppl_backend_test.py +13 -0
  146. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-gnu.so +0 -0
  147. sage/numerical/backends/scip_backend.pxd +22 -0
  148. sage/numerical/backends/scip_backend.pyx +1289 -0
  149. sage/numerical/backends/scip_backend_test.py +13 -0
  150. sage/numerical/interactive_simplex_method.py +5338 -0
  151. sage/numerical/knapsack.py +665 -0
  152. sage/numerical/linear_functions.cpython-314-x86_64-linux-gnu.so +0 -0
  153. sage/numerical/linear_functions.pxd +31 -0
  154. sage/numerical/linear_functions.pyx +1648 -0
  155. sage/numerical/linear_tensor.py +470 -0
  156. sage/numerical/linear_tensor_constraints.py +448 -0
  157. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-gnu.so +0 -0
  158. sage/numerical/linear_tensor_element.pxd +6 -0
  159. sage/numerical/linear_tensor_element.pyx +459 -0
  160. sage/numerical/mip.cpython-314-x86_64-linux-gnu.so +0 -0
  161. sage/numerical/mip.pxd +40 -0
  162. sage/numerical/mip.pyx +3667 -0
  163. sage/numerical/sdp.cpython-314-x86_64-linux-gnu.so +0 -0
  164. sage/numerical/sdp.pxd +39 -0
  165. sage/numerical/sdp.pyx +1433 -0
  166. sage/rings/all__sagemath_polyhedra.py +3 -0
  167. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  168. sage/rings/polynomial/omega.py +982 -0
  169. sage/schemes/all__sagemath_polyhedra.py +2 -0
  170. sage/schemes/toric/all.py +10 -0
  171. sage/schemes/toric/chow_group.py +1248 -0
  172. sage/schemes/toric/divisor.py +2082 -0
  173. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-gnu.so +0 -0
  174. sage/schemes/toric/divisor_class.pyx +322 -0
  175. sage/schemes/toric/fano_variety.py +1606 -0
  176. sage/schemes/toric/homset.py +650 -0
  177. sage/schemes/toric/ideal.py +451 -0
  178. sage/schemes/toric/library.py +1322 -0
  179. sage/schemes/toric/morphism.py +1958 -0
  180. sage/schemes/toric/points.py +1032 -0
  181. sage/schemes/toric/sheaf/all.py +1 -0
  182. sage/schemes/toric/sheaf/constructor.py +302 -0
  183. sage/schemes/toric/sheaf/klyachko.py +921 -0
  184. sage/schemes/toric/toric_subscheme.py +905 -0
  185. sage/schemes/toric/variety.py +3460 -0
  186. sage/schemes/toric/weierstrass.py +1078 -0
  187. sage/schemes/toric/weierstrass_covering.py +457 -0
  188. sage/schemes/toric/weierstrass_higher.py +288 -0
  189. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  190. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  194. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  195. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  205. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  206. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,1986 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ """
3
+ Functions for plotting polyhedra
4
+ """
5
+
6
+ ########################################################################
7
+ # Copyright (C) 2008 Marshall Hampton <hamptonio@gmail.com>
8
+ # Copyright (C) 2011 Volker Braun <vbraun.name@gmail.com>
9
+ #
10
+ # Distributed under the terms of the GNU General Public License (GPL)
11
+ #
12
+ # https://www.gnu.org/licenses/
13
+ ########################################################################
14
+
15
+ from math import pi
16
+
17
+ from sage.misc.lazy_import import lazy_import
18
+ from sage.structure.sage_object import SageObject
19
+ from sage.modules.free_module_element import vector
20
+ from sage.matrix.constructor import matrix, identity_matrix
21
+ from sage.matrix.special import diagonal_matrix
22
+ from sage.misc.functional import norm
23
+ from sage.misc.latex import LatexExpr
24
+ from sage.structure.sequence import Sequence
25
+
26
+ lazy_import("sage.plot.all", ["Graphics", "point2d", "line2d", "arrow", "polygon2d", "rainbow"])
27
+ lazy_import("sage.plot.plot3d.all", ["point3d", "line3d", "arrow3d", "polygons3d"])
28
+ lazy_import("sage.plot.plot3d.transform", "rotate_arbitrary")
29
+ lazy_import("sage.plot.plot3d.texture", "Texture")
30
+
31
+
32
+ #############################################################
33
+
34
+
35
+ def cyclic_sort_vertices_2d(Vlist):
36
+ """
37
+ Return the vertices/rays in cyclic order if possible.
38
+
39
+ .. NOTE::
40
+
41
+ This works if and only if each vertex/ray is adjacent to exactly
42
+ two others. For example, any 2-dimensional polyhedron satisfies
43
+ this.
44
+
45
+ See
46
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_adjacency_matrix`
47
+ for a discussion of "adjacent".
48
+
49
+ EXAMPLES::
50
+
51
+ sage: from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d
52
+ sage: square = Polyhedron([[1,0],[-1,0],[0,1],[0,-1]])
53
+ sage: vertices = [v for v in square.vertex_generator()]
54
+ sage: vertices
55
+ [A vertex at (-1, 0),
56
+ A vertex at (0, -1),
57
+ A vertex at (0, 1),
58
+ A vertex at (1, 0)]
59
+ sage: cyclic_sort_vertices_2d(vertices)
60
+ [A vertex at (1, 0),
61
+ A vertex at (0, -1),
62
+ A vertex at (-1, 0),
63
+ A vertex at (0, 1)]
64
+
65
+ Rays are allowed, too::
66
+
67
+ sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (2, 0), (3, 0), (4, 1)], rays=[(0,1)])
68
+ sage: P.adjacency_matrix()
69
+ [0 1 0 1 0]
70
+ [1 0 1 0 0]
71
+ [0 1 0 0 1]
72
+ [1 0 0 0 1]
73
+ [0 0 1 1 0]
74
+ sage: cyclic_sort_vertices_2d(P.Vrepresentation())
75
+ [A vertex at (3, 0),
76
+ A vertex at (1, 0),
77
+ A vertex at (0, 1),
78
+ A ray in the direction (0, 1),
79
+ A vertex at (4, 1)]
80
+
81
+ sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (2, 0), (3, 0), (4, 1)], rays=[(0,1), (1,1)])
82
+ sage: P.adjacency_matrix()
83
+ [0 1 0 0 0]
84
+ [1 0 1 0 0]
85
+ [0 1 0 0 1]
86
+ [0 0 0 0 1]
87
+ [0 0 1 1 0]
88
+ sage: cyclic_sort_vertices_2d(P.Vrepresentation())
89
+ [A ray in the direction (1, 1),
90
+ A vertex at (3, 0),
91
+ A vertex at (1, 0),
92
+ A vertex at (0, 1),
93
+ A ray in the direction (0, 1)]
94
+
95
+ sage: P = Polyhedron(vertices=[(1,2)], rays=[(0,1)], lines=[(1,0)])
96
+ sage: P.adjacency_matrix()
97
+ [0 0 1]
98
+ [0 0 0]
99
+ [1 0 0]
100
+ sage: cyclic_sort_vertices_2d(P.Vrepresentation())
101
+ [A vertex at (0, 2),
102
+ A line in the direction (1, 0),
103
+ A ray in the direction (0, 1)]
104
+ """
105
+ if not Vlist:
106
+ return Vlist
107
+ Vlist = list(Vlist)
108
+ result = []
109
+ adjacency_matrix = Vlist[0].polyhedron().vertex_adjacency_matrix()
110
+
111
+ # Any object in Vlist has 0,1, or 2 adjacencies. Break into connected chains:
112
+ chain = [Vlist.pop()]
113
+ while Vlist:
114
+ first_index = chain[0].index()
115
+ last_index = chain[-1].index()
116
+ for v in Vlist:
117
+ v_index = v.index()
118
+ if adjacency_matrix[last_index, v_index] == 1:
119
+ chain = chain + [v]
120
+ Vlist.remove(v)
121
+ break
122
+ if adjacency_matrix[first_index, v.index()] == 1:
123
+ chain = [v] + chain
124
+ Vlist.remove(v)
125
+ break
126
+ else:
127
+ result += chain
128
+ chain = [Vlist.pop()]
129
+ result += chain
130
+ return result
131
+
132
+ #########################################################################
133
+
134
+
135
+ def projection_func_identity(x):
136
+ """
137
+ The identity projection.
138
+
139
+ EXAMPLES::
140
+
141
+ sage: from sage.geometry.polyhedron.plot import projection_func_identity
142
+ sage: projection_func_identity((1,2,3))
143
+ [1, 2, 3]
144
+ """
145
+ return list(x)
146
+
147
+
148
+ class ProjectionFuncStereographic:
149
+ """
150
+ The stereographic (or perspective) projection onto a codimension-1 linear
151
+ subspace with respect to a sphere centered at the origin.
152
+
153
+ EXAMPLES::
154
+
155
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
156
+ sage: cube = polytopes.hypercube(3).vertices()
157
+ sage: proj = ProjectionFuncStereographic([1.2, 3.4, 5.6])
158
+ sage: ppoints = [proj(vector(x)) for x in cube]
159
+ sage: ppoints[5]
160
+ (-0.0918273..., -0.036375...)
161
+ """
162
+ def __init__(self, projection_point):
163
+ """
164
+ Create a stereographic projection function.
165
+
166
+ INPUT:
167
+
168
+ - ``projection_point`` -- list of coordinates in the
169
+ appropriate dimension, which is the point projected from
170
+
171
+ EXAMPLES::
172
+
173
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
174
+ sage: proj = ProjectionFuncStereographic([1.0,1.0])
175
+ sage: proj.__init__([1.0,1.0])
176
+ sage: proj.house
177
+ [ 0.7071067811... -0.7071067811...]
178
+ [ 0.7071067811... 0.7071067811...]
179
+ sage: TestSuite(proj).run(skip='_test_pickling')
180
+ """
181
+ from sage.rings.real_double import RDF
182
+
183
+ self.projection_point = vector(projection_point)
184
+ self.dim = self.projection_point.degree()
185
+
186
+ pproj = vector(RDF, self.projection_point)
187
+ self.psize = norm(pproj)
188
+ if (self.psize).is_zero():
189
+ raise ValueError("projection direction must be a nonzero vector.")
190
+ v = vector(RDF, [0.0] * (self.dim - 1) + [-self.psize]) - pproj
191
+ polediff = matrix(RDF, v).transpose()
192
+ denom = RDF((polediff.transpose() * polediff)[0][0])
193
+ if denom.is_zero():
194
+ self.house = identity_matrix(RDF, self.dim)
195
+ else:
196
+ house = identity_matrix(RDF, self.dim) \
197
+ - 2*polediff*polediff.transpose()/denom # Householder reflector
198
+ # Make it preserve orientation (chirality):
199
+ self.house = diagonal_matrix(RDF, [1] * (self.dim - 1) + [-1]) * house
200
+
201
+ def __call__(self, x):
202
+ """
203
+ Action of the stereographic projection.
204
+
205
+ INPUT:
206
+
207
+ - ``x`` -- a vector or anything convertible to a vector
208
+
209
+ OUTPUT:
210
+
211
+ First reflects ``x`` with a Householder reflection which takes
212
+ the projection point to ``(0,...,0,self.psize)`` where
213
+ ``psize`` is the length of the projection point, and then
214
+ dilates by ``1/(zdiff)`` where ``zdiff`` is the difference
215
+ between the last coordinate of ``x`` and ``psize``.
216
+
217
+ EXAMPLES::
218
+
219
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
220
+ sage: proj = ProjectionFuncStereographic([1.0,1.0])
221
+ sage: proj.__call__(vector([1,2]))
222
+ (1.0000000000000002)
223
+ sage: proj = ProjectionFuncStereographic([2.0,1.0])
224
+ sage: proj.__call__(vector([1,2])) # abs tol 1e-14
225
+ (-2.999999999999997)
226
+ sage: proj = ProjectionFuncStereographic([0,0,2])
227
+ sage: proj.__call__(vector([0,0,1]))
228
+ (0.0, 0.0)
229
+ sage: proj.__call__(vector([1,0,0]))
230
+ (0.5, 0.0)
231
+ """
232
+ from sage.rings.real_double import RDF
233
+
234
+ img = self.house * x
235
+ denom = self.psize - img[self.dim - 1]
236
+ if denom.is_zero():
237
+ raise ValueError('Point cannot coincide with '
238
+ 'coordinate singularity at ' + repr(x))
239
+ return vector(RDF, [img[i] / denom for i in range(self.dim - 1)])
240
+
241
+
242
+ class ProjectionFuncSchlegel:
243
+ """
244
+ The Schlegel projection from the given input point.
245
+
246
+ EXAMPLES::
247
+
248
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
249
+ sage: fcube = polytopes.hypercube(4)
250
+ sage: facet = fcube.facets()[0]
251
+ sage: proj = ProjectionFuncSchlegel(facet,[0,-1.5,0,0])
252
+ sage: proj([0,0,0,0])[0]
253
+ 1.0
254
+ """
255
+ def __init__(self, facet, projection_point):
256
+ """
257
+ Initialize the projection.
258
+
259
+ EXAMPLES::
260
+
261
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
262
+ sage: fcube = polytopes.hypercube(4)
263
+ sage: facet = fcube.facets()[0]
264
+ sage: proj = ProjectionFuncSchlegel(facet,[0,-1.5,0,0])
265
+ sage: proj.facet
266
+ A 3-dimensional face of a Polyhedron in ZZ^4 defined as the convex hull of 8 vertices
267
+ sage: proj.A
268
+ [1.0 0.0 0.0]
269
+ [0.0 0.0 0.0]
270
+ [0.0 0.0 1.0]
271
+ [0.0 1.0 0.0]
272
+ sage: proj.b
273
+ (1.0, 1.0, 1.0)
274
+ sage: proj.projection_point
275
+ (0.0, -1.5, 0.0, 0.0)
276
+ sage: proj([-1,1,1,1])
277
+ (0.8, 1.2, 1.2)
278
+ sage: proj([1,1,1,1])
279
+ (1.2, 1.2, 1.2)
280
+ sage: proj([1,-1,1,1])
281
+ (2.0, 2.0, 2.0)
282
+ sage: proj([1,-1,-1,1])
283
+ (2.0, 2.0, 0.0)
284
+ sage: TestSuite(proj).run(skip='_test_pickling')
285
+ """
286
+ from sage.rings.real_double import RDF
287
+
288
+ self.facet = facet
289
+ ineq = [h for h in facet.ambient_Hrepresentation() if h.is_inequality()][0]
290
+ self.full_A = ineq.A()
291
+ self.full_b = ineq.b()
292
+ A, b = self.facet.as_polyhedron().affine_hull_projection(as_affine_map=True, orthonormal=True, extend=True)
293
+ self.A = A.change_ring(RDF).matrix()
294
+ self.b = b.change_ring(RDF)
295
+ self.projection_point = vector(RDF, projection_point)
296
+
297
+ def __call__(self, x):
298
+ """
299
+ Apply the projection to a vector.
300
+
301
+ - ``x`` -- a vector or anything convertible to a vector
302
+
303
+ EXAMPLES::
304
+
305
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel
306
+ sage: fcube = polytopes.hypercube(4)
307
+ sage: facet = fcube.facets()[0]
308
+ sage: proj = ProjectionFuncSchlegel(facet,[0,-1.5,0,0])
309
+ sage: proj.__call__([0,0,0,0])
310
+ (1.0, 1.0, 1.0)
311
+ """
312
+ # The intersection of the segment with the facet
313
+ # See Ziegler's "Lectures on Polytopes" p.133
314
+ vx = vector(x)
315
+ z = (self.full_b)
316
+ a = -(self.full_A)
317
+ y = self.projection_point
318
+ preimage = y + ((z-a*y)/(a*vx-a*y))*(vx - y)
319
+ # The transformation matrix acts on the right:
320
+ return preimage*self.A + self.b
321
+
322
+ #########################################################################
323
+
324
+
325
+ class Projection(SageObject):
326
+ """
327
+ The projection of a :class:`Polyhedron`.
328
+
329
+ This class keeps track of the necessary data to plot the input
330
+ polyhedron.
331
+ """
332
+
333
+ def __init__(self, polyhedron, proj=projection_func_identity):
334
+ """
335
+ Initialize the projection of a Polyhedron() object.
336
+
337
+ INPUT:
338
+
339
+ - ``polyhedron`` -- a ``Polyhedron()`` object
340
+
341
+ - ``proj`` -- a projection function for the points
342
+
343
+ .. NOTE::
344
+
345
+ Once initialized, the polyhedral data is fixed. However, the
346
+ projection can be changed later on.
347
+
348
+ EXAMPLES::
349
+
350
+ sage: # needs sage.groups
351
+ sage: p = polytopes.icosahedron(exact=False)
352
+ sage: from sage.geometry.polyhedron.plot import Projection
353
+ sage: Projection(p)
354
+ The projection of a polyhedron into 3 dimensions
355
+ sage: def pr_12(x): return [x[1],x[2]]
356
+ sage: Projection(p, pr_12)
357
+ The projection of a polyhedron into 2 dimensions
358
+ sage: Projection(p, lambda x: [x[1],x[2]] ) # another way of doing the same projection
359
+ The projection of a polyhedron into 2 dimensions
360
+ sage: _.plot() # plot of the projected icosahedron in 2d # needs sage.plot
361
+ Graphics object consisting of 51 graphics primitives
362
+ sage: proj = Projection(p)
363
+ sage: proj.stereographic([1,2,3])
364
+ The projection of a polyhedron into 2 dimensions
365
+ sage: proj.plot() # needs sage.plot
366
+ Graphics object consisting of 51 graphics primitives
367
+ sage: TestSuite(proj).run(skip='_test_pickling')
368
+ """
369
+ self.parent_polyhedron = polyhedron
370
+ self.coords = Sequence([])
371
+ self.points = Sequence([])
372
+ self.lines = Sequence([])
373
+ self.arrows = Sequence([])
374
+ self.polygons = Sequence([])
375
+ self.polyhedron_ambient_dim = polyhedron.ambient_dim()
376
+ self.polyhedron_dim = polyhedron.dim()
377
+
378
+ if polyhedron.ambient_dim() == 2:
379
+ self._init_from_2d(polyhedron)
380
+ elif polyhedron.ambient_dim() == 3:
381
+ self._init_from_3d(polyhedron)
382
+ else:
383
+ self._init_points(polyhedron)
384
+ self._init_lines_arrows(polyhedron)
385
+
386
+ self.coords.set_immutable()
387
+ self.points.set_immutable()
388
+ self.lines.set_immutable()
389
+ self.arrows.set_immutable()
390
+ self.polygons.set_immutable()
391
+
392
+ self(proj)
393
+
394
+ def _repr_(self) -> str:
395
+ """
396
+ Return a string describing the projection.
397
+
398
+ EXAMPLES::
399
+
400
+ sage: p = polytopes.hypercube(3)
401
+ sage: from sage.geometry.polyhedron.plot import Projection
402
+ sage: proj = Projection(p)
403
+ sage: print(proj._repr_())
404
+ The projection of a polyhedron into 3 dimensions
405
+ """
406
+ s = 'The projection of a polyhedron into '
407
+ s += repr(self.dimension) + ' dimensions'
408
+ return s
409
+
410
+ def __call__(self, proj=projection_func_identity):
411
+ """
412
+ Apply a projection.
413
+
414
+ EXAMPLES::
415
+
416
+ sage: # needs sage.groups
417
+ sage: p = polytopes.icosahedron(exact=False)
418
+ sage: from sage.geometry.polyhedron.plot import Projection
419
+ sage: pproj = Projection(p)
420
+ sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic
421
+ sage: pproj_stereo = pproj.__call__(proj=ProjectionFuncStereographic([1, 2, 3]))
422
+ sage: sorted(pproj_stereo.polygons)
423
+ [[2, 0, 9],
424
+ [3, 1, 10],
425
+ [4, 0, 8],
426
+ ...
427
+ [11, 1, 3],
428
+ [11, 3, 7],
429
+ [11, 7, 9]]
430
+ """
431
+ self.transformed_coords = Sequence([proj(p) for p in self.coords])
432
+ self._init_dimension()
433
+ return self
434
+
435
+ def identity(self):
436
+ """
437
+ Return the identity projection of the polyhedron.
438
+
439
+ EXAMPLES::
440
+
441
+ sage: # needs sage.groups
442
+ sage: p = polytopes.icosahedron(exact=False)
443
+ sage: from sage.geometry.polyhedron.plot import Projection
444
+ sage: pproj = Projection(p)
445
+ sage: ppid = pproj.identity()
446
+ sage: ppid.dimension
447
+ 3
448
+ """
449
+ return self(projection_func_identity)
450
+
451
+ def stereographic(self, projection_point=None):
452
+ r"""
453
+ Return the stereographic projection.
454
+
455
+ INPUT:
456
+
457
+ - ``projection_point`` -- the projection point. This must be
458
+ distinct from the polyhedron's vertices. Default is `(1,0,\dots,0)`.
459
+
460
+ EXAMPLES::
461
+
462
+ sage: from sage.geometry.polyhedron.plot import Projection
463
+ sage: proj = Projection(polytopes.buckyball()); proj # long time
464
+ The projection of a polyhedron into 3 dimensions
465
+ sage: proj.stereographic([5,2,3]).plot() # long time # needs sage.plot
466
+ Graphics object consisting of 123 graphics primitives
467
+ sage: Projection(polytopes.twenty_four_cell()).stereographic([2,0,0,0])
468
+ The projection of a polyhedron into 3 dimensions
469
+ """
470
+ if projection_point is None:
471
+ projection_point = [1] + [0]*(self.polyhedron_ambient_dim-1)
472
+ return self(ProjectionFuncStereographic(projection_point))
473
+
474
+ def schlegel(self, facet=None, position=None):
475
+ r"""
476
+ Return the Schlegel projection.
477
+
478
+ * The facet is orthonormally transformed into its affine hull.
479
+
480
+ * The position specifies a point coming out of the barycenter of the
481
+ facet from which the other vertices will be projected into the facet.
482
+
483
+ INPUT:
484
+
485
+ - ``facet`` -- a PolyhedronFace; the facet into which the Schlegel
486
+ diagram is created. The default is the first facet.
487
+
488
+ - ``position`` -- a positive number. Determines a relative distance
489
+ from the barycenter of ``facet``. A value close to 0 will place the
490
+ projection point close to the facet and a large value further away.
491
+ If the given value is too large, an error is returned.
492
+ If no position is given, it takes the midpoint of the possible
493
+ point of views along a line spanned by the barycenter of the facet
494
+ and a valid point outside the facet.
495
+
496
+ EXAMPLES::
497
+
498
+ sage: cube4 = polytopes.hypercube(4)
499
+ sage: from sage.geometry.polyhedron.plot import Projection
500
+ sage: Projection(cube4).schlegel()
501
+ The projection of a polyhedron into 3 dimensions
502
+ sage: _.plot() # needs sage.plot
503
+ Graphics3d Object
504
+
505
+ The 4-cube with a truncated vertex seen into the resulting tetrahedron
506
+ facet::
507
+
508
+ sage: tcube4 = cube4.face_truncation(cube4.faces(0)[0])
509
+ sage: tcube4.facets()[4]
510
+ A 3-dimensional face of a Polyhedron in QQ^4 defined as the convex hull of 4 vertices
511
+ sage: into_tetra = Projection(tcube4).schlegel(tcube4.facets()[4]) # needs sage.symbolic
512
+ sage: into_tetra.plot() # needs sage.plot sage.symbolic
513
+ Graphics3d Object
514
+
515
+ Taking a larger value for the position changes the image::
516
+
517
+ sage: into_tetra_far = Projection(tcube4).schlegel(tcube4.facets()[4], 4) # needs sage.symbolic
518
+ sage: into_tetra_far.plot() # needs sage.plot sage.symbolic
519
+ Graphics3d Object
520
+
521
+ A value which is too large or negative give a projection point that
522
+ sees more than one facet resulting in a error::
523
+
524
+ sage: Projection(tcube4).schlegel(tcube4.facets()[4], 5)
525
+ Traceback (most recent call last):
526
+ ...
527
+ ValueError: the chosen position is too large
528
+ sage: Projection(tcube4).schlegel(tcube4.facets()[4], -1)
529
+ Traceback (most recent call last):
530
+ ...
531
+ ValueError: 'position' should be a positive number
532
+ """
533
+ from sage.geometry.polyhedron.face import PolyhedronFace
534
+ from sage.rings.integer_ring import ZZ
535
+ if facet is None:
536
+ facet = self.parent_polyhedron.facets()[0]
537
+ elif not isinstance(facet, PolyhedronFace):
538
+ raise TypeError("{} should be a PolyhedronFace of {}".format(facet, self))
539
+ elif facet.dim() != self.parent_polyhedron.dim() - 1:
540
+ raise ValueError("The face should be a facet of the polyhedron")
541
+ if position is not None and position <= 0:
542
+ raise ValueError("'position' should be a positive number")
543
+
544
+ barycenter = ZZ.one() * sum([v.vector() for v in facet.vertices()]) / len(facet.vertices())
545
+ locus_polyhedron = facet.stacking_locus()
546
+ repr_point = locus_polyhedron.representative_point()
547
+ if position is None:
548
+ # Figure out a somehow canonical point of view inside the locus
549
+ # polyhedron
550
+ from sage.geometry.polyhedron.constructor import Polyhedron
551
+ the_ray = Polyhedron(vertices=[barycenter],
552
+ rays=[repr_point - barycenter],
553
+ backend=locus_polyhedron.backend()) & locus_polyhedron
554
+ projection_point = the_ray.representative_point()
555
+ else:
556
+ projection_point = (1-position)*barycenter + position*repr_point
557
+ if not locus_polyhedron.relative_interior_contains(projection_point):
558
+ raise ValueError("the chosen position is too large")
559
+
560
+ return self(ProjectionFuncSchlegel(facet, projection_point))
561
+
562
+ def coord_index_of(self, v):
563
+ """
564
+ Convert a coordinate vector to its internal index.
565
+
566
+ EXAMPLES::
567
+
568
+ sage: p = polytopes.hypercube(3)
569
+ sage: proj = p.projection()
570
+ sage: proj.coord_index_of(vector((1,1,1)))
571
+ 2
572
+ """
573
+ try:
574
+ return self.coords.index(v)
575
+ except ValueError:
576
+ self.coords.append(v)
577
+ return len(self.coords)-1
578
+
579
+ def coord_indices_of(self, v_list):
580
+ """
581
+ Convert list of coordinate vectors to the corresponding list
582
+ of internal indices.
583
+
584
+ EXAMPLES::
585
+
586
+ sage: p = polytopes.hypercube(3)
587
+ sage: proj = p.projection()
588
+ sage: proj.coord_indices_of([vector((1,1,1)), vector((1,-1,1))])
589
+ [2, 3]
590
+ """
591
+ return [self.coord_index_of(v) for v in v_list]
592
+
593
+ def coordinates_of(self, coord_index_list):
594
+ """
595
+ Given a list of indices, return the projected coordinates.
596
+
597
+ EXAMPLES::
598
+
599
+ sage: p = polytopes.simplex(4, project=True).projection() # needs cddexec
600
+ sage: p.coordinates_of([1]) # needs cddexec
601
+ [[-0.7071067812, 0.4082482905, 0.2886751346, 0.2236067977]]
602
+ """
603
+ return [self.transformed_coords[i] for i in coord_index_list]
604
+
605
+ def _init_dimension(self):
606
+ """
607
+ Internal function: Initialize from polyhedron with
608
+ projected coordinates. Must always be called after
609
+ a coordinate projection.
610
+
611
+ TESTS::
612
+
613
+ sage: p = polytopes.simplex(2, project=True).projection() # needs cddexec
614
+ sage: test = p._init_dimension() # needs cddexec
615
+ sage: p.plot.__doc__ == p.render_2d.__doc__ # needs cddexec
616
+ True
617
+ """
618
+ if self.transformed_coords:
619
+ self.dimension = len(self.transformed_coords[0])
620
+ else:
621
+ self.dimension = 0
622
+ if self.dimension == 0:
623
+ self.plot = self.render_0d
624
+ elif self.dimension == 1:
625
+ self.plot = self.render_1d
626
+ elif self.dimension == 2:
627
+ self.plot = self.render_2d
628
+ elif self.dimension == 3:
629
+ self.plot = self.render_3d
630
+ else:
631
+ try:
632
+ del self.plot
633
+ except AttributeError:
634
+ pass
635
+
636
+ def _init_from_2d(self, polyhedron):
637
+ """
638
+ Internal function: Initialize from polyhedron in
639
+ 2-dimensional space. The polyhedron could be lower
640
+ dimensional.
641
+
642
+ TESTS::
643
+
644
+ sage: p = Polyhedron(vertices=[[0,0],[0,1],[1,0],[1,1]])
645
+ sage: proj = p.projection()
646
+ sage: [proj.coordinates_of([i]) for i in proj.points]
647
+ [[[0, 0]], [[0, 1]], [[1, 0]], [[1, 1]]]
648
+ sage: proj._init_from_2d
649
+ <bound method Projection._init_from_2d of The projection
650
+ of a polyhedron into 2 dimensions>
651
+ """
652
+ assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"
653
+ self.dimension = 2
654
+ self._init_points(polyhedron)
655
+ self._init_lines_arrows(polyhedron)
656
+ self._init_area_2d(polyhedron)
657
+
658
+ def _init_from_3d(self, polyhedron):
659
+ """
660
+ Internal function: Initialize from polyhedron in
661
+ 3-dimensional space. The polyhedron could be
662
+ lower dimensional.
663
+
664
+ TESTS::
665
+
666
+ sage: p = Polyhedron(vertices=[[0,0,1],[0,1,2],[1,0,3],[1,1,5]])
667
+ sage: proj = p.projection()
668
+ sage: [proj.coordinates_of([i]) for i in proj.points]
669
+ [[[0, 0, 1]], [[0, 1, 2]], [[1, 0, 3]], [[1, 1, 5]]]
670
+ sage: proj._init_from_3d
671
+ <bound method Projection._init_from_3d of The projection
672
+ of a polyhedron into 3 dimensions>
673
+ """
674
+ assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"
675
+ self.dimension = 3
676
+ self._init_points(polyhedron)
677
+ self._init_lines_arrows(polyhedron)
678
+ self._init_solid_3d(polyhedron)
679
+
680
+ def _init_points(self, polyhedron):
681
+ """
682
+ Internal function: Initialize points (works in arbitrary
683
+ dimensions).
684
+
685
+ TESTS::
686
+
687
+ sage: p = polytopes.hypercube(2)
688
+ sage: pp = p.projection()
689
+ sage: del pp.points
690
+ sage: pp.points = Sequence([])
691
+ sage: pp._init_points(p)
692
+ sage: pp.points
693
+ [0, 1, 2, 3]
694
+ """
695
+ for v in polyhedron.vertex_generator():
696
+ self.points.append(self.coord_index_of(v.vector()))
697
+
698
+ def _init_lines_arrows(self, polyhedron):
699
+ """
700
+ Internal function: Initialize compact and non-compact edges
701
+ (works in arbitrary dimensions).
702
+
703
+ TESTS::
704
+
705
+ sage: p = Polyhedron(ieqs=[[1, 0, 0, 1], [1, 1, 0, 0]])
706
+ sage: pp = p.projection()
707
+ sage: pp.arrows
708
+ [[0, 1], [0, 2], [0, 3], [0, 4]]
709
+ sage: del pp.arrows
710
+ sage: pp.arrows = Sequence([])
711
+ sage: pp._init_lines_arrows(p)
712
+ sage: pp.arrows
713
+ [[0, 1], [0, 2], [0, 3], [0, 4]]
714
+
715
+ We check that :issue:`31802` is fixed::
716
+
717
+ sage: x = Polyhedron(lines=[(1, 0, 0), (0, 1, 0)], rays=[(0, 0, 1)])
718
+ sage: y = x.projection()
719
+ sage: del y.arrows
720
+ sage: y.arrows = Sequence([])
721
+ sage: y._init_lines_arrows(x)
722
+ sage: y.arrows
723
+ [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5]]
724
+ """
725
+ obj = polyhedron.Vrepresentation()
726
+ adj_matrix = polyhedron.vertex_adjacency_matrix()
727
+ for i in range(len(obj)):
728
+ if not obj[i].is_vertex():
729
+ if any(adj_matrix[i, j] != 0
730
+ for j in range(len(obj))):
731
+ continue
732
+ # obj[i] is ray or line
733
+ v = polyhedron.vertices()[0].vector()
734
+ r = obj[i].vector()
735
+ self.arrows.append( [ self.coord_index_of(v),
736
+ self.coord_index_of(v + r) ] )
737
+ if obj[i].is_line():
738
+ self.arrows.append( [ self.coord_index_of(v),
739
+ self.coord_index_of(v - r) ] )
740
+ for j in range(len(obj)):
741
+ if adj_matrix[i, j] == 0:
742
+ continue
743
+ if i < j and obj[j].is_vertex():
744
+ l = [obj[i].vector(), obj[j].vector()]
745
+ self.lines.append( [ self.coord_index_of(l[0]),
746
+ self.coord_index_of(l[1]) ] )
747
+ if obj[j].is_ray():
748
+ l = [obj[i].vector(), obj[i].vector() + obj[j].vector()]
749
+ self.arrows.append( [ self.coord_index_of(l[0]),
750
+ self.coord_index_of(l[1]) ] )
751
+ if obj[j].is_line():
752
+ l1 = [obj[i].vector(), obj[i].vector() + obj[j].vector()]
753
+ l2 = [obj[i].vector(), obj[i].vector() - obj[j].vector()]
754
+ self.arrows.append( [ self.coord_index_of(l1[0]),
755
+ self.coord_index_of(l1[1]) ] )
756
+ self.arrows.append( [ self.coord_index_of(l2[0]),
757
+ self.coord_index_of(l2[1]) ] )
758
+
759
+ def _init_area_2d(self, polyhedron):
760
+ """
761
+ Internal function: Initialize polygon area for 2d polyhedron.
762
+
763
+ TESTS::
764
+
765
+ sage: p = polytopes.cyclic_polytope(2,4)
766
+ sage: proj = p.projection()
767
+ sage: proj.polygons = Sequence([])
768
+ sage: proj._init_area_2d(p)
769
+ sage: proj.polygons
770
+ [[3, 0, 1, 2]]
771
+ """
772
+ assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"
773
+ vertices = list(polyhedron.Vrep_generator())
774
+ vertices = cyclic_sort_vertices_2d(vertices)
775
+ coords = []
776
+
777
+ def adjacent_vertices(i):
778
+ n = len(vertices)
779
+ if vertices[(i-1) % n].is_vertex():
780
+ yield vertices[(i-1) % n]
781
+ if vertices[(i+1) % n].is_vertex():
782
+ yield vertices[(i+1) % n]
783
+
784
+ for i in range(len(vertices)):
785
+ v = vertices[i]
786
+ if v.is_vertex():
787
+ coords.append(v())
788
+ if v.is_ray():
789
+ for a in adjacent_vertices(i):
790
+ coords.append(a() + v())
791
+
792
+ if polyhedron.n_lines() == 0:
793
+ self.polygons.append(self.coord_indices_of(coords))
794
+ return
795
+
796
+ polygons = []
797
+
798
+ if polyhedron.n_lines() == 1:
799
+ a_line = next(polyhedron.line_generator())
800
+ for shift in [a_line(), -a_line()]:
801
+ for i in range(len(coords)):
802
+ polygons.append([coords[i - 1], coords[i],
803
+ coords[i] + shift, coords[i - 1] + shift])
804
+
805
+ if polyhedron.n_lines() == 2:
806
+ line1, line2 = polyhedron.lines()
807
+ assert len(coords) == 1, "Can have only a single vertex!"
808
+ v = coords[0]
809
+ l1 = line1()
810
+ l2 = line2()
811
+ polygons = [[v-l1-l2, v+l1-l2, v+l1+l2, v-l1+l2]]
812
+
813
+ polygons = [self.coord_indices_of(p) for p in polygons]
814
+ self.polygons.extend(polygons)
815
+
816
+ def _init_solid_3d(self, polyhedron):
817
+ """
818
+ Internal function: Initialize facet polygons for 3d polyhedron.
819
+
820
+ TESTS::
821
+
822
+ sage: p = polytopes.cyclic_polytope(3,4)
823
+ sage: proj = p.projection()
824
+ sage: proj.polygons = Sequence([])
825
+ sage: proj._init_solid_3d(p)
826
+ sage: proj.polygons
827
+ [[1, 0, 2], [3, 0, 1], [2, 0, 3], [3, 1, 2]]
828
+
829
+ sage: x = Polyhedron(rays=[(-1, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)])
830
+ sage: y = x.projection()
831
+ sage: y.polygons
832
+ [[5, 2, 1, 6], [2, 7, 8, 1]]
833
+
834
+ sage: cylinder = Polyhedron(vertices=[(0, 0, 0), (1, 0, 0), (0, 1, 0)], lines=[(0, 0, 1)])
835
+ sage: len(cylinder.projection().polygons)
836
+ 3
837
+ """
838
+ assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"
839
+
840
+ if polyhedron.dim() <= 1: # empty or 0d or 1d polyhedron => no polygon
841
+ return None
842
+
843
+ def defining_equation(): # corresponding to a polygon
844
+ if polyhedron.dim() < 3:
845
+ yield next(polyhedron.equation_generator())
846
+ else:
847
+ yield from polyhedron.inequality_generator()
848
+
849
+ faces = []
850
+ face_inequalities = []
851
+ for facet_equation in defining_equation():
852
+ vertices = list(facet_equation.incident())
853
+ face_inequalities.append(facet_equation)
854
+ vertices = cyclic_sort_vertices_2d(vertices)
855
+ if len(vertices) >= 3:
856
+ v0, v1, v2 = (vector(v) for v in vertices[:3])
857
+ normal = (v2 - v0).cross_product(v1 - v0)
858
+ if normal.dot_product(facet_equation.A()) < 0:
859
+ vertices.reverse()
860
+ coords = []
861
+
862
+ def adjacent_vertices(i):
863
+ n = len(vertices)
864
+ if vertices[(i-1) % n].is_vertex():
865
+ yield vertices[(i-1) % n]
866
+ if vertices[(i+1) % n].is_vertex():
867
+ yield vertices[(i+1) % n]
868
+
869
+ for i in range(len(vertices)):
870
+ v = vertices[i]
871
+ if v.is_vertex():
872
+ coords.append(v())
873
+ if v.is_ray():
874
+ for a in adjacent_vertices(i):
875
+ coords.append(a() + v())
876
+
877
+ faces.append(coords)
878
+ self.face_inequalities = face_inequalities
879
+
880
+ if polyhedron.n_lines() == 0:
881
+ assert faces, "no vertices?"
882
+ self.polygons.extend( [self.coord_indices_of(f) for f in faces] )
883
+ return
884
+
885
+ # now some special cases if there are lines (dim < ambient_dim)
886
+ polygons = []
887
+
888
+ if polyhedron.n_lines() == 1:
889
+ assert faces, "no vertices?"
890
+ a_line = next(polyhedron.line_generator())
891
+ shift = a_line()
892
+ for coords in faces:
893
+ assert len(coords) == 2, "There must be two points."
894
+ polygons.append([coords[0] - shift, coords[1] - shift,
895
+ coords[1] + shift, coords[0] + shift])
896
+
897
+ if polyhedron.n_lines() == 2:
898
+ line1, line2 = polyhedron.line_generator()
899
+ l1 = line1()
900
+ l2 = line2()
901
+ for v in polyhedron.vertex_generator():
902
+ polygons.append([v()-l1-l2, v()+l1-l2, v()+l1+l2, v()-l1+l2])
903
+
904
+ self.polygons.extend([self.coord_indices_of(p) for p in polygons])
905
+
906
+ def render_points_1d(self, **kwds):
907
+ """
908
+ Return the points of a polyhedron in 1d.
909
+
910
+ INPUT:
911
+
912
+ - ``**kwds`` -- options passed through to
913
+ :func:`~sage.plot.point.point2d`
914
+
915
+ OUTPUT: a 2-d graphics object
916
+
917
+ EXAMPLES::
918
+
919
+ sage: cube1 = polytopes.hypercube(1)
920
+ sage: proj = cube1.projection()
921
+ sage: points = proj.render_points_1d() # needs sage.plot
922
+ sage: points._objects # needs sage.plot
923
+ [Point set defined by 2 point(s)]
924
+ """
925
+ return point2d([c + [0] for c in self.coordinates_of(self.points)], **kwds)
926
+
927
+ def render_line_1d(self, **kwds):
928
+ """
929
+ Return the line of a polyhedron in 1d.
930
+
931
+ INPUT:
932
+
933
+ - ``**kwds`` -- options passed through to
934
+ :func:`~sage.plot.line.line2d`
935
+
936
+ OUTPUT: a 2-d graphics object
937
+
938
+ EXAMPLES::
939
+
940
+ sage: outline = polytopes.hypercube(1).projection().render_line_1d() # needs sage.plot
941
+ sage: outline._objects[0] # needs sage.plot
942
+ Line defined by 2 points
943
+ """
944
+ if len(self.lines) == 0:
945
+ return Graphics()
946
+ elif len(self.lines) == 1:
947
+ line = self.coordinates_of(self.lines[0])
948
+ return line2d([line[0] + [0], line[1] + [0]], **kwds)
949
+ else:
950
+ assert False # unreachable
951
+
952
+ def render_points_2d(self, **kwds):
953
+ """
954
+ Return the points of a polyhedron in 2d.
955
+
956
+ EXAMPLES::
957
+
958
+ sage: # needs sage.rings.number_field
959
+ sage: hex = polytopes.regular_polygon(6)
960
+ sage: proj = hex.projection()
961
+ sage: hex_points = proj.render_points_2d() # needs sage.plot
962
+ sage: hex_points._objects # needs sage.plot
963
+ [Point set defined by 6 point(s)]
964
+ """
965
+ return point2d(self.coordinates_of(self.points), **kwds)
966
+
967
+ def render_outline_2d(self, **kwds):
968
+ """
969
+ Return the outline (edges) of a polyhedron in 2d.
970
+
971
+ EXAMPLES::
972
+
973
+ sage: penta = polytopes.regular_polygon(5) # needs sage.rings.number_field
974
+ sage: outline = penta.projection().render_outline_2d() # needs sage.plot sage.rings.number_field
975
+ sage: outline._objects[0] # needs sage.plot sage.rings.number_field
976
+ Line defined by 2 points
977
+ """
978
+ wireframe = []
979
+ for l in self.lines:
980
+ l_coords = self.coordinates_of(l)
981
+ wireframe.append(line2d(l_coords, **kwds))
982
+ for a in self.arrows:
983
+ a_coords = self.coordinates_of(a)
984
+ wireframe.append(arrow(a_coords[0], a_coords[1], **kwds))
985
+ return sum(wireframe)
986
+
987
+ def render_fill_2d(self, **kwds):
988
+ """
989
+ Return the filled interior (a polygon) of a polyhedron in 2d.
990
+
991
+ EXAMPLES::
992
+
993
+ sage: cps = [i^3 for i in srange(-2, 2, 1/5)]
994
+ sage: p = Polyhedron(vertices=[[(t^2-1)/(t^2+1), 2*t/(t^2+1)] for t in cps])
995
+ sage: proj = p.projection()
996
+ sage: filled_poly = proj.render_fill_2d() # needs sage.plot
997
+ sage: filled_poly.axes_width() # needs sage.plot
998
+ 0.8
999
+ """
1000
+ poly = [polygon2d(self.coordinates_of(p), **kwds)
1001
+ for p in self.polygons]
1002
+ return sum(poly)
1003
+
1004
+ def render_vertices_3d(self, **kwds):
1005
+ """
1006
+ Return the 3d rendering of the vertices.
1007
+
1008
+ EXAMPLES::
1009
+
1010
+ sage: p = polytopes.cross_polytope(3)
1011
+ sage: proj = p.projection()
1012
+ sage: verts = proj.render_vertices_3d() # needs sage.plot
1013
+ sage: verts.bounding_box() # needs sage.plot
1014
+ ((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0))
1015
+ """
1016
+ return point3d(self.coordinates_of(self.points), **kwds)
1017
+
1018
+ def render_wireframe_3d(self, **kwds):
1019
+ r"""
1020
+ Return the 3d wireframe rendering.
1021
+
1022
+ EXAMPLES::
1023
+
1024
+ sage: cube = polytopes.hypercube(3)
1025
+ sage: cube_proj = cube.projection()
1026
+ sage: wire = cube_proj.render_wireframe_3d() # needs sage.plot
1027
+ sage: print(wire.tachyon().split('\n')[77]) # for testing # needs sage.plot
1028
+ FCylinder base 1.0 1.0 -1.0 apex -1.0 1.0 -1.0 rad 0.005 texture...
1029
+ """
1030
+ wireframe = []
1031
+ for l in self.lines:
1032
+ l_coords = self.coordinates_of(l)
1033
+ wireframe.append(line3d(l_coords, **kwds))
1034
+ for a in self.arrows:
1035
+ a_coords = self.coordinates_of(a)
1036
+ wireframe.append(arrow3d(a_coords[0], a_coords[1], **kwds))
1037
+ return sum(wireframe)
1038
+
1039
+ def render_solid_3d(self, **kwds):
1040
+ """
1041
+ Return solid 3d rendering of a 3d polytope.
1042
+
1043
+ EXAMPLES::
1044
+
1045
+ sage: p = polytopes.hypercube(3).projection()
1046
+ sage: p_solid = p.render_solid_3d(opacity=.7) # needs sage.plot
1047
+ sage: type(p_solid) # needs sage.plot
1048
+ <class 'sage.plot.plot3d.index_face_set.IndexFaceSet'>
1049
+ """
1050
+ polys = self.polygons
1051
+ n = len(polys)
1052
+ N = max([-1] + [i for p in polys for i in p]) + 1
1053
+ coords = self.coordinates_of(range(N))
1054
+ col = kwds.pop('color', (0, 0, 1))
1055
+ if col == 'rainbow':
1056
+ t_list = [Texture(rainbow(n, 'rgbtuple')[i]) for i in range(n)]
1057
+ return polygons3d(polys, coords, texture_list=t_list, **kwds)
1058
+ else:
1059
+ return polygons3d(polys, coords, color=col, **kwds)
1060
+
1061
+ def render_0d(self, point_opts=None, line_opts=None, polygon_opts=None):
1062
+ """
1063
+ Return 0d rendering of the projection of a polyhedron into
1064
+ 2-dimensional ambient space.
1065
+
1066
+ INPUT:
1067
+
1068
+ See
1069
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`.
1070
+
1071
+ OUTPUT: a 2-d graphics object
1072
+
1073
+ EXAMPLES::
1074
+
1075
+ sage: print(Polyhedron([]).projection().render_0d().description()) # needs sage.plot
1076
+ <BLANKLINE>
1077
+ sage: P = Polyhedron(ieqs=[(1,)])
1078
+ sage: print(P.projection().render_0d().description()) # needs sage.plot
1079
+ Point set defined by 1 point(s): [(0.0, 0.0)]
1080
+ """
1081
+ if point_opts is None:
1082
+ point_opts = {}
1083
+ if isinstance(point_opts, dict):
1084
+ point_opts.setdefault('zorder', 2)
1085
+ point_opts.setdefault('pointsize', 10)
1086
+ if self.points:
1087
+ return point2d([0, 0], **point_opts)
1088
+ else:
1089
+ return Graphics()
1090
+
1091
+ def render_1d(self, point_opts=None, line_opts=None, polygon_opts=None):
1092
+ """
1093
+ Return 1d rendering of the projection of a polyhedron into
1094
+ 2-dimensional ambient space.
1095
+
1096
+ INPUT:
1097
+
1098
+ See
1099
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.plot`.
1100
+
1101
+ OUTPUT: a 2-d graphics object
1102
+
1103
+ EXAMPLES::
1104
+
1105
+ sage: Polyhedron([(0,), (1,)]).projection().render_1d() # needs sage.plot
1106
+ Graphics object consisting of 2 graphics primitives
1107
+ """
1108
+ plt = Graphics()
1109
+ if point_opts is None:
1110
+ point_opts = {}
1111
+ if line_opts is None:
1112
+ line_opts = {}
1113
+ if isinstance(point_opts, dict):
1114
+ point_opts.setdefault('zorder', 2)
1115
+ point_opts.setdefault('pointsize', 10)
1116
+ plt += self.render_points_1d(**point_opts)
1117
+ if isinstance(line_opts, dict):
1118
+ line_opts.setdefault('zorder', 1)
1119
+ plt += self.render_line_1d(**line_opts)
1120
+ return plt
1121
+
1122
+ def render_2d(self, point_opts=None, line_opts=None, polygon_opts=None):
1123
+ """
1124
+ Return 2d rendering of the projection of a polyhedron into
1125
+ 2-dimensional ambient space.
1126
+
1127
+ EXAMPLES::
1128
+
1129
+ sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]])
1130
+ sage: q1 = p1.projection()
1131
+ sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]])
1132
+ sage: q2 = p2.projection()
1133
+ sage: p3 = Polyhedron(vertices=[[1,2]])
1134
+ sage: q3 = p3.projection()
1135
+ sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]])
1136
+ sage: q4 = p4.projection()
1137
+ sage: q1.plot() + q2.plot() + q3.plot() + q4.plot() # needs sage.plot
1138
+ Graphics object consisting of 18 graphics primitives
1139
+ """
1140
+ plt = Graphics()
1141
+ if point_opts is None:
1142
+ point_opts = {}
1143
+ if line_opts is None:
1144
+ line_opts = {}
1145
+ if polygon_opts is None:
1146
+ polygon_opts = {}
1147
+ if isinstance(point_opts, dict):
1148
+ point_opts.setdefault('zorder', 2)
1149
+ point_opts.setdefault('pointsize', 10)
1150
+ plt += self.render_points_2d(**point_opts)
1151
+ if isinstance(line_opts, dict):
1152
+ line_opts.setdefault('zorder', 1)
1153
+ plt += self.render_outline_2d(**line_opts)
1154
+ if isinstance(polygon_opts, dict):
1155
+ polygon_opts.setdefault('zorder', 0)
1156
+ plt += self.render_fill_2d(**polygon_opts)
1157
+ return plt
1158
+
1159
+ def render_3d(self, point_opts=None, line_opts=None, polygon_opts=None):
1160
+ """
1161
+ Return 3d rendering of a polyhedron projected into
1162
+ 3-dimensional ambient space.
1163
+
1164
+ EXAMPLES::
1165
+
1166
+ sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]])
1167
+ sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]])
1168
+ sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]],
1169
+ ....: rays=[[-1,-1,-1]])
1170
+ sage: (p1.projection().plot() + p2.projection().plot() # needs sage.plot
1171
+ ....: + p3.projection().plot())
1172
+ Graphics3d Object
1173
+
1174
+ It correctly handles various degenerate cases::
1175
+
1176
+ sage: # needs sage.plot
1177
+ sage: Polyhedron(lines=[[1,0,0], [0,1,0], [0,0,1]]).plot() # whole space
1178
+ Graphics3d Object
1179
+ sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]],
1180
+ ....: lines=[[0,1,0], [0,0,1]]).plot() # half space
1181
+ Graphics3d Object
1182
+ sage: Polyhedron(lines=[[0,1,0], [0,0,1]],
1183
+ ....: vertices=[[1,1,1]]).plot() # R^2 in R^3
1184
+ Graphics3d Object
1185
+ sage: Polyhedron(rays=[[0,1,0], [0,0,1]], # quadrant wedge in R^2
1186
+ ....: lines=[[1,0,0]]).plot()
1187
+ Graphics3d Object
1188
+ sage: Polyhedron(rays=[[0,1,0]], # upper half plane in R^3
1189
+ ....: lines=[[1,0,0]]).plot()
1190
+ Graphics3d Object
1191
+ sage: Polyhedron(lines=[[1,0,0]]).plot() # R^1 in R^2
1192
+ Graphics3d Object
1193
+ sage: Polyhedron(rays=[[0,1,0]]).plot() # Half-line in R^3
1194
+ Graphics3d Object
1195
+ sage: Polyhedron(vertices=[[1,1,1]]).plot() # point in R^3
1196
+ Graphics3d Object
1197
+
1198
+ The origin is not included, if it is not in the polyhedron (:issue:`23555`)::
1199
+
1200
+ sage: Q = Polyhedron([[100],[101]])
1201
+ sage: P = Q*Q*Q; P
1202
+ A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
1203
+ sage: p = P.plot() # needs sage.plot
1204
+ sage: p.bounding_box() # needs sage.plot
1205
+ ((100.0, 100.0, 100.0), (101.0, 101.0, 101.0))
1206
+
1207
+ Plot 3d polytope with rainbow colors::
1208
+
1209
+ sage: polytopes.hypercube(3).plot(polygon='rainbow', alpha=0.4) # needs sage.plot
1210
+ Graphics3d Object
1211
+ """
1212
+ pplt = None
1213
+ lplt = None
1214
+ pgplt = None
1215
+ if point_opts is None:
1216
+ point_opts = {}
1217
+ if line_opts is None:
1218
+ line_opts = {}
1219
+ if polygon_opts is None:
1220
+ polygon_opts = {}
1221
+ if isinstance(point_opts, dict):
1222
+ point_opts.setdefault('size', 10)
1223
+ pplt = self.render_vertices_3d(**point_opts)
1224
+ if isinstance(line_opts, dict):
1225
+ line_opts.setdefault('width', 1) # controls the width of arrow3d for a ray
1226
+ line_opts.setdefault('thickness', 1) # controls the thickness of line3d
1227
+ lplt = self.render_wireframe_3d(**line_opts)
1228
+ if isinstance(polygon_opts, dict):
1229
+ if 'threejs_flat_shading' not in polygon_opts:
1230
+ polygon_opts['threejs_flat_shading'] = True
1231
+ pgplt = self.render_solid_3d(**polygon_opts)
1232
+ # zorder is not available
1233
+ return sum(_ for _ in [pplt, lplt, pgplt] if _ is not None)
1234
+
1235
+ def tikz(self, view=[0, 0, 1], angle=0, scale=1,
1236
+ edge_color='blue!95!black', facet_color='blue!95!black',
1237
+ opacity=0.8, vertex_color='green', axis=False,
1238
+ output_type='TikzPicture'):
1239
+ r"""
1240
+ Return a tikz picture of ``self`` as a string or as a
1241
+ :class:`~sage.misc.latex_standalone.TikzPicture`
1242
+ according to a projection ``view`` and an angle ``angle``
1243
+ obtained via the threejs viewer.
1244
+
1245
+ INPUT:
1246
+
1247
+ - ``view`` -- list (default: [0,0,1]) representing the rotation axis (see note below)
1248
+ - ``angle`` -- integer (default: 0); angle of rotation in degree from 0 to 360 (see note
1249
+ below)
1250
+ - ``scale`` -- integer (default: 1); the scaling of the tikz picture
1251
+ - ``edge_color`` -- string (default: ``'blue!95!black'``); representing colors which tikz
1252
+ recognizes
1253
+ - ``facet_color`` -- string (default: ``'blue!95!black'``); representing colors which tikz
1254
+ recognizes
1255
+ - ``vertex_color`` -- string (default: ``'green'``); representing colors which tikz
1256
+ recognizes
1257
+ - ``opacity`` -- real number (default: 0.8) between 0 and 1 giving the opacity of
1258
+ the front facets
1259
+ - ``axis`` -- boolean (default: ``False``); draw the axes at the origin or not
1260
+ - ``output_type`` -- string (default: ``'TikzPicture'``); valid values
1261
+ are ``'LatexExpr'`` and ``'TikzPicture'``,
1262
+ whether to return a :class:`LatexExpr` object (which inherits from Python
1263
+ :class:`str`) or a :class:`TikzPicture` object from module
1264
+ :mod:`sage.misc.latex_standalone`
1265
+
1266
+ OUTPUT: :class:`LatexExpr` object or :class:`TikzPicture` object
1267
+
1268
+ .. NOTE::
1269
+
1270
+ The inputs ``view`` and ``angle`` can be obtained by visualizing it
1271
+ using ``.show(aspect_ratio=1)``. This will open an interactive view
1272
+ in your default browser, where you can rotate the polytope. Once
1273
+ the desired view angle is found, click on the information icon in
1274
+ the lower right-hand corner and select *Get Viewpoint*. This will
1275
+ copy a string of the form '[x,y,z],angle' to your local clipboard.
1276
+ Go back to Sage and type ``Img = P.projection().tikz([x,y,z],angle)``.
1277
+
1278
+ The inputs ``view`` and ``angle`` can also be obtained from the
1279
+ viewer Jmol::
1280
+
1281
+ 1) Right click on the image
1282
+ 2) Select ``Console``
1283
+ 3) Select the tab ``State``
1284
+ 4) Scroll to the line ``moveto``
1285
+
1286
+ It reads something like::
1287
+
1288
+ moveto 0.0 {x y z angle} Scale
1289
+
1290
+ The ``view`` is then [x,y,z] and ``angle`` is angle.
1291
+ The following number is the scale.
1292
+
1293
+ Jmol performs a rotation of ``angle`` degrees along the
1294
+ vector [x,y,z] and show the result from the z-axis.
1295
+
1296
+ EXAMPLES::
1297
+
1298
+ sage: # needs sage.plot sage.rings.number_field
1299
+ sage: P1 = polytopes.small_rhombicuboctahedron()
1300
+ sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4,
1301
+ ....: output_type='TikzPicture')
1302
+ sage: type(Image1)
1303
+ <class 'sage.misc.latex_standalone.TikzPicture'>
1304
+ sage: Image1
1305
+ \documentclass[tikz]{standalone}
1306
+ \begin{document}
1307
+ \begin{tikzpicture}%
1308
+ [x={(-0.939161cm, 0.244762cm)},
1309
+ y={(0.097442cm, -0.482887cm)},
1310
+ z={(0.329367cm, 0.840780cm)},
1311
+ scale=4.000000,
1312
+ ...
1313
+ Use print to see the full content.
1314
+ ...
1315
+ \node[vertex] at (-2.41421, 1.00000, -1.00000) {};
1316
+ \node[vertex] at (-2.41421, -1.00000, 1.00000) {};
1317
+ %%
1318
+ %%
1319
+ \end{tikzpicture}
1320
+ \end{document}
1321
+ sage: _ = Image1.tex('polytope-tikz1.tex') # not tested
1322
+ sage: _ = Image1.png('polytope-tikz1.png') # not tested
1323
+ sage: _ = Image1.pdf('polytope-tikz1.pdf') # not tested
1324
+ sage: _ = Image1.svg('polytope-tikz1.svg') # not tested
1325
+
1326
+ A second example::
1327
+
1328
+ sage: P2 = Polyhedron(vertices=[[1, 1], [1, 2], [2, 1]])
1329
+ sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black',
1330
+ ....: facet_color='orange!95!black', opacity=0.4,
1331
+ ....: vertex_color='yellow', axis=True,
1332
+ ....: output_type='TikzPicture')
1333
+ sage: Image2
1334
+ \documentclass[tikz]{standalone}
1335
+ \begin{document}
1336
+ \begin{tikzpicture}%
1337
+ [scale=3.000000,
1338
+ back/.style={loosely dotted, thin},
1339
+ edge/.style={color=blue!95!black, thick},
1340
+ facet/.style={fill=orange!95!black,fill opacity=0.400000},
1341
+ ...
1342
+ Use print to see the full content.
1343
+ ...
1344
+ \node[vertex] at (1.00000, 2.00000) {};
1345
+ \node[vertex] at (2.00000, 1.00000) {};
1346
+ %%
1347
+ %%
1348
+ \end{tikzpicture}
1349
+ \end{document}
1350
+
1351
+ The second example using a LatexExpr as output type::
1352
+
1353
+ sage: # needs sage.plot
1354
+ sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black',
1355
+ ....: facet_color='orange!95!black', opacity=0.4,
1356
+ ....: vertex_color='yellow', axis=True,
1357
+ ....: output_type='LatexExpr')
1358
+ sage: type(Image2)
1359
+ <class 'sage.misc.latex.LatexExpr'>
1360
+ sage: print('\n'.join(Image2.splitlines()[:4]))
1361
+ \begin{tikzpicture}%
1362
+ [scale=3.000000,
1363
+ back/.style={loosely dotted, thin},
1364
+ edge/.style={color=blue!95!black, thick},
1365
+ sage: with open('polytope-tikz2.tex', 'w') as f: # not tested
1366
+ ....: _ = f.write(Image2)
1367
+
1368
+ A third example::
1369
+
1370
+ sage: # needs sage.plot
1371
+ sage: P3 = Polyhedron(vertices=[[-1, -1, 2], [-1, 2, -1], [2, -1, -1]]); P3
1372
+ A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices
1373
+ sage: Image3 = P3.projection().tikz([0.5, -1, -0.1], 55, scale=3,
1374
+ ....: edge_color='blue!95!black',
1375
+ ....: facet_color='orange!95!black', opacity=0.7,
1376
+ ....: vertex_color='yellow', axis=True)
1377
+ sage: Image3
1378
+ \documentclass[tikz]{standalone}
1379
+ \begin{document}
1380
+ \begin{tikzpicture}%
1381
+ [x={(0.658184cm, -0.242192cm)},
1382
+ y={(-0.096240cm, 0.912008cm)},
1383
+ z={(-0.746680cm, -0.331036cm)},
1384
+ scale=3.000000,
1385
+ ...
1386
+ Use print to see the full content.
1387
+ ...
1388
+ \node[vertex] at (-1.00000, 2.00000, -1.00000) {};
1389
+ \node[vertex] at (2.00000, -1.00000, -1.00000) {};
1390
+ %%
1391
+ %%
1392
+ \end{tikzpicture}
1393
+ \end{document}
1394
+ sage: _ = Image3.tex('polytope-tikz3.tex') # not tested
1395
+ sage: _ = Image3.png('polytope-tikz3.png') # not tested
1396
+ sage: _ = Image3.pdf('polytope-tikz3.pdf') # not tested
1397
+ sage: _ = Image3.svg('polytope-tikz3.svg') # not tested
1398
+
1399
+ A fourth example::
1400
+
1401
+ sage: P = Polyhedron(vertices=[[1,1,0,0], [1,2,0,0],
1402
+ ....: [2,1,0,0], [0,0,1,0], [0,0,0,1]]); P
1403
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
1404
+ sage: P.projection().tikz(output_type='TikzPicture')
1405
+ Traceback (most recent call last):
1406
+ ...
1407
+ NotImplementedError: The polytope has to live in 2 or 3 dimensions.
1408
+
1409
+ TESTS::
1410
+
1411
+ sage: P = Polyhedron(vertices=[[0,0,0], [1,0,0],
1412
+ ....: [0,0,1], [0,1,0]])
1413
+ sage: P.projection().tikz(output_type='kawai') # needs sage.plot
1414
+ Traceback (most recent call last):
1415
+ ...
1416
+ ValueError: output_type (='kawai') must be 'LatexExpr' or 'TikzPicture'
1417
+
1418
+ .. TODO::
1419
+
1420
+ Make it possible to draw Schlegel diagram for 4-polytopes. ::
1421
+
1422
+ sage: P = Polyhedron(vertices=[[1,1,0,0], [1,2,0,0],
1423
+ ....: [2,1,0,0], [0,0,1,0], [0,0,0,1]]); P
1424
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices
1425
+ sage: P.projection().tikz(output_type='TikzPicture')
1426
+ Traceback (most recent call last):
1427
+ ...
1428
+ NotImplementedError: The polytope has to live in 2 or 3 dimensions.
1429
+
1430
+ Make it possible to draw 3-polytopes living in higher dimension.
1431
+ """
1432
+ if self.polyhedron_ambient_dim > 3 or self.polyhedron_ambient_dim < 2:
1433
+ raise NotImplementedError("The polytope has to live in 2 or 3 dimensions.")
1434
+ elif self.polyhedron_dim < 2 or self.polyhedron_dim > 3:
1435
+ raise NotImplementedError("The polytope has to be 2 or 3-dimensional.")
1436
+ elif self.polyhedron_ambient_dim == 2: # self is a polygon in 2-space
1437
+ tikz_string = self._tikz_2d(scale, edge_color,
1438
+ facet_color, opacity,
1439
+ vertex_color, axis)
1440
+ elif self.polyhedron_dim == 2: # self is a polygon in 3-space
1441
+ tikz_string = self._tikz_2d_in_3d(view, angle, scale, edge_color,
1442
+ facet_color, opacity,
1443
+ vertex_color, axis)
1444
+ else: # self is a 3-polytope in 3-space
1445
+ tikz_string = self._tikz_3d_in_3d(view, angle, scale, edge_color,
1446
+ facet_color, opacity,
1447
+ vertex_color, axis)
1448
+
1449
+ # return
1450
+ if output_type == 'LatexExpr':
1451
+ return tikz_string
1452
+
1453
+ if output_type == 'TikzPicture':
1454
+ from sage.misc.latex_standalone import TikzPicture
1455
+ return TikzPicture(tikz_string, standalone_config=None,
1456
+ usepackage=None, usetikzlibrary=None,
1457
+ macros=None, use_sage_preamble=False)
1458
+
1459
+ raise ValueError("output_type (='{}') must be 'LatexExpr' or"
1460
+ " 'TikzPicture'".format(output_type))
1461
+
1462
+ def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis):
1463
+ r"""
1464
+ Return a string ``tikz_pic`` consisting of a tikz picture of
1465
+ ``self``, which is assumed to be a polygon on the plane.
1466
+
1467
+ INPUT:
1468
+
1469
+ - ``scale`` -- integer specifying the scaling of the tikz picture
1470
+ - ``edge_color`` -- string representing colors which tikz
1471
+ recognizes
1472
+ - ``facet_color`` -- string representing colors which tikz
1473
+ recognizes
1474
+ - ``vertex_color`` -- string representing colors which tikz
1475
+ recognizes
1476
+ - ``opacity`` -- real number between 0 and 1 giving the opacity of
1477
+ the front facets
1478
+ - ``axis`` -- boolean (default: ``False``); draw the axes at the origin or not
1479
+
1480
+ OUTPUT:
1481
+
1482
+ :class:`LatexExpr` -- containing the TikZ picture.
1483
+
1484
+ EXAMPLES::
1485
+
1486
+ sage: P = Polyhedron(vertices=[[1, 1], [1, 2], [2, 1]])
1487
+ sage: Image = P.projection()._tikz_2d(scale=3, edge_color='black',
1488
+ ....: facet_color='orange', opacity=0.75,
1489
+ ....: vertex_color='yellow', axis=True)
1490
+ sage: type(Image)
1491
+ <class 'sage.misc.latex.LatexExpr'>
1492
+ sage: print('\n'.join(Image.splitlines()[:4]))
1493
+ \begin{tikzpicture}%
1494
+ [scale=3.000000,
1495
+ back/.style={loosely dotted, thin},
1496
+ edge/.style={color=black, thick},
1497
+ sage: with open('polytope-tikz2.tex', 'w') as f: # not tested
1498
+ ....: _ = f.write(Image)
1499
+
1500
+ Scientific notation is not used in the output (:issue:`16519`)::
1501
+
1502
+ sage: P = Polyhedron([[2*10^-10,0], [0,1], [1,0]], base_ring=QQ)
1503
+ sage: tikz = P.projection().tikz(output_type='TikzPicture')
1504
+ sage: 'e-10' in tikz.content()
1505
+ False
1506
+
1507
+ .. NOTE::
1508
+
1509
+ The ``facet_color`` is the filing color of the polytope (polygon).
1510
+ """
1511
+
1512
+ # Creates the nodes, coordinate and tag for every vertex of the polytope.
1513
+ # The tag is used to draw the front facets later on.
1514
+
1515
+ dict_drawing = {}
1516
+ edges = ''
1517
+ for vert in self.points:
1518
+ v = self.coords[vert]
1519
+ v_vect = str(['%.5f' % i for i in v]).replace('\'', '')
1520
+ v_vect = v_vect.replace('[', '(')
1521
+ v_vect = v_vect.replace(']', ')')
1522
+ tag = '%s' % v_vect
1523
+ node = "\\node[%s] at %s {};\n" % ('vertex', tag)
1524
+ coord = '\\coordinate %s at %s;\n' % (tag, tag)
1525
+ dict_drawing[vert] = node, coord, tag
1526
+
1527
+ for index1, index2 in self.lines:
1528
+ # v1 = self.coords[index1]
1529
+ # v2 = self.coords[index2]
1530
+ edges += "\\draw[%s] %s -- %s;\n" % ('edge',
1531
+ dict_drawing[index1][2],
1532
+ dict_drawing[index2][2])
1533
+
1534
+ # Start to write the output
1535
+ tikz_pic = ''
1536
+ tikz_pic += '\\begin{tikzpicture}%\n'
1537
+ tikz_pic += '\t[scale=%f,\n' % scale
1538
+ tikz_pic += '\tback/.style={loosely dotted, thin},\n'
1539
+ tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color
1540
+ tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color, opacity)
1541
+ tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color
1542
+ tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color
1543
+
1544
+ # Gives the reproduction information
1545
+ from sage.env import SAGE_VERSION
1546
+ tikz_pic += "%% This TikZ-picture was produced with Sagemath version {}\n".format(SAGE_VERSION)
1547
+ tikz_pic += "%% with the command: ._tikz_2d and parameters:\n"
1548
+ tikz_pic += "%% scale = {}\n".format(scale)
1549
+ tikz_pic += "%% edge_color = {}\n".format(edge_color)
1550
+ tikz_pic += "%% facet_color = {}\n".format(facet_color)
1551
+ tikz_pic += "%% opacity = {}\n".format(opacity)
1552
+ tikz_pic += "%% vertex_color = {}\n".format(vertex_color)
1553
+ tikz_pic += "%% axis = {}\n%%\n".format(axis)
1554
+
1555
+ # Draws the axes if True
1556
+ if axis:
1557
+ tikz_pic += '%% Drawing the axes\n'
1558
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
1559
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n\n'
1560
+
1561
+ # Create the coordinate of the vertices:
1562
+ tikz_pic += '%% Coordinate of the vertices:\n%%\n'
1563
+ for v in dict_drawing:
1564
+ tikz_pic += dict_drawing[v][1]
1565
+
1566
+ # Draw the interior by going in a cycle
1567
+ vertices = list(self.parent_polyhedron.Vrep_generator())
1568
+ tikz_pic += '%%\n%%\n%% Drawing the interior\n%%\n'
1569
+ cyclic_vert = cyclic_sort_vertices_2d(list(self.parent_polyhedron.Vrep_generator()))
1570
+ cyclic_indices = [vertices.index(v) for v in cyclic_vert]
1571
+ tikz_pic += '\\fill[facet] '
1572
+ for v in cyclic_indices:
1573
+ if v in dict_drawing:
1574
+ tikz_pic += '%s -- ' % dict_drawing[v][2]
1575
+ tikz_pic += 'cycle {};\n'
1576
+
1577
+ # Draw the edges
1578
+ tikz_pic += '%%\n%%\n%% Drawing edges\n%%\n'
1579
+ tikz_pic += edges
1580
+
1581
+ # Finally, the vertices in front are drawn on top of everything.
1582
+ tikz_pic += '%%\n%%\n%% Drawing the vertices\n%%\n'
1583
+ for v in dict_drawing:
1584
+ tikz_pic += dict_drawing[v][0]
1585
+ tikz_pic += '%%\n%%\n\\end{tikzpicture}'
1586
+
1587
+ return LatexExpr(tikz_pic)
1588
+
1589
+ def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color,
1590
+ opacity, vertex_color, axis):
1591
+ r"""
1592
+ Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
1593
+ according to a projection ``view`` and an angle ``angle``
1594
+ obtained via Jmol through the current state property. ``self`` is
1595
+ assumed to be a polygon in 3-space.
1596
+
1597
+ INPUT:
1598
+
1599
+ - ``view`` -- list (default: [0,0,1]) representing the rotation axis
1600
+ - ``angle`` -- integer angle of rotation in degree from 0 to 360
1601
+ - ``scale`` -- integer specifying the scaling of the tikz picture
1602
+ - ``edge_color`` -- string representing colors which tikz
1603
+ recognizes
1604
+ - ``facet_color`` -- string representing colors which tikz
1605
+ recognizes
1606
+ - ``vertex_color`` -- string representing colors which tikz
1607
+ recognizes
1608
+ - ``opacity`` -- real number between 0 and 1 giving the opacity of
1609
+ the front facets
1610
+ - ``axis`` -- boolean draw the axes at the origin or not
1611
+
1612
+ OUTPUT: :class:`LatexExpr` -- containing the TikZ picture
1613
+
1614
+ EXAMPLES::
1615
+
1616
+ sage: # needs sage.plot
1617
+ sage: P = Polyhedron(vertices=[[-1, -1, 2], [-1, 2, -1], [2, -1, -1]]); P
1618
+ A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices
1619
+ sage: Image = P.projection()._tikz_2d_in_3d(view=[0.5, -1, -0.5], angle=55, scale=3,
1620
+ ....: edge_color='blue!95!black', facet_color='orange',
1621
+ ....: opacity=0.5, vertex_color='yellow', axis=True)
1622
+ sage: print('\n'.join(Image.splitlines()[:4]))
1623
+ \begin{tikzpicture}%
1624
+ [x={(0.644647cm, -0.476559cm)},
1625
+ y={(0.192276cm, 0.857859cm)},
1626
+ z={(-0.739905cm, -0.192276cm)},
1627
+ sage: with open('polytope-tikz3.tex', 'w') as f: # not tested
1628
+ ....: _ = f.write(Image)
1629
+
1630
+ ::
1631
+
1632
+ sage: p = Polyhedron(vertices=[[1, 0, 0], [0, 1, 0], [0, 0, 1]])
1633
+ sage: proj = p.projection()
1634
+ sage: Img = proj.tikz([1, 1, 1], 130, axis=True, output_type='LatexExpr') # needs sage.plot
1635
+ sage: print('\n'.join(Img.splitlines()[12:21])) # needs sage.plot
1636
+ %% with the command: ._tikz_2d_in_3d and parameters:
1637
+ %% view = [1, 1, 1]
1638
+ %% angle = 130
1639
+ %% scale = 1
1640
+ %% edge_color = blue!95!black
1641
+ %% facet_color = blue!95!black
1642
+ %% opacity = 0.8
1643
+ %% vertex_color = green
1644
+ %% axis = True
1645
+
1646
+ .. NOTE::
1647
+
1648
+ The ``facet_color`` is the filing color of the polytope (polygon).
1649
+ """
1650
+ from sage.rings.real_double import RDF
1651
+
1652
+ view_vector = vector(RDF, view)
1653
+ rot = rotate_arbitrary(view_vector, -(angle/360)*2*pi)
1654
+ rotation_matrix = rot[:2].transpose()
1655
+
1656
+ # Creates the nodes, coordinate and tag for every vertex of the polytope.
1657
+ # The tag is used to draw the front facets later on.
1658
+ dict_drawing = {}
1659
+ edges = ''
1660
+ for vert in self.points:
1661
+ v = self.coords[vert]
1662
+ v_vect = str(['%.5f' % i for i in v]).replace('\'', '')
1663
+ v_vect = v_vect.replace('[', '(')
1664
+ v_vect = v_vect.replace(']', ')')
1665
+ tag = '%s' % v_vect
1666
+ node = "\\node[%s] at %s {};\n" % ('vertex', tag)
1667
+ coord = '\\coordinate %s at %s;\n' % (tag, tag)
1668
+ dict_drawing[vert] = node, coord, tag
1669
+
1670
+ for index1, index2 in self.lines:
1671
+ # v1 = self.coords[index1]
1672
+ # v2 = self.coords[index2]
1673
+ edges += "\\draw[%s] %s -- %s;\n" % ('edge',
1674
+ dict_drawing[index1][2],
1675
+ dict_drawing[index2][2])
1676
+
1677
+ # Start to write the output
1678
+ tikz_pic = ''
1679
+ tikz_pic += '\\begin{tikzpicture}%\n'
1680
+ tikz_pic += '\t[x={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[0][0]),
1681
+ RDF(rotation_matrix[0][1]))
1682
+ tikz_pic += '\ty={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[1][0]),
1683
+ RDF(rotation_matrix[1][1]))
1684
+ tikz_pic += '\tz={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[2][0]),
1685
+ RDF(rotation_matrix[2][1]))
1686
+ tikz_pic += '\tscale=%f,\n' % scale
1687
+ tikz_pic += '\tback/.style={loosely dotted, thin},\n'
1688
+ tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color
1689
+ tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color, opacity)
1690
+ tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color
1691
+ tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color
1692
+
1693
+ # Gives the reproduction information
1694
+ from sage.env import SAGE_VERSION
1695
+ tikz_pic += "%% This TikZ-picture was produced with Sagemath version {}\n".format(SAGE_VERSION)
1696
+ tikz_pic += "%% with the command: ._tikz_2d_in_3d and parameters:\n"
1697
+ tikz_pic += "%% view = {}\n".format(view)
1698
+ tikz_pic += "%% angle = {}\n".format(angle)
1699
+ tikz_pic += "%% scale = {}\n".format(scale)
1700
+ tikz_pic += "%% edge_color = {}\n".format(edge_color)
1701
+ tikz_pic += "%% facet_color = {}\n".format(facet_color)
1702
+ tikz_pic += "%% opacity = {}\n".format(opacity)
1703
+ tikz_pic += "%% vertex_color = {}\n".format(vertex_color)
1704
+ tikz_pic += "%% axis = {}\n%%\n".format(axis)
1705
+
1706
+ # Draws the axes if True
1707
+ if axis:
1708
+ tikz_pic += '%% Drawing the axes\n'
1709
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
1710
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'
1711
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'
1712
+
1713
+ # Create the coordinate of the vertices:
1714
+ tikz_pic += '%% Coordinate of the vertices:\n%%\n'
1715
+ for v in dict_drawing:
1716
+ tikz_pic += dict_drawing[v][1]
1717
+
1718
+ # Draw the interior by going in a cycle
1719
+ vertices = list(self.parent_polyhedron.Vrep_generator())
1720
+ tikz_pic += '%%\n%%\n%% Drawing the interior\n%%\n'
1721
+ cyclic_vert = cyclic_sort_vertices_2d(list(self.parent_polyhedron.Vrep_generator()))
1722
+ cyclic_indices = [vertices.index(v) for v in cyclic_vert]
1723
+ tikz_pic += '\\fill[facet] '
1724
+ for v in cyclic_indices:
1725
+ if v in dict_drawing:
1726
+ tikz_pic += '%s -- ' % dict_drawing[v][2]
1727
+ tikz_pic += 'cycle {};\n'
1728
+
1729
+ # Draw the edges in the front
1730
+ tikz_pic += '%%\n%%\n%% Drawing edges\n%%\n'
1731
+ tikz_pic += edges
1732
+
1733
+ # Finally, the vertices in front are drawn on top of everything.
1734
+ tikz_pic += '%%\n%%\n%% Drawing the vertices\n%%\n'
1735
+ for v in dict_drawing:
1736
+ tikz_pic += dict_drawing[v][0]
1737
+ tikz_pic += '%%\n%%\n\\end{tikzpicture}'
1738
+
1739
+ return LatexExpr(tikz_pic)
1740
+
1741
+ def _tikz_3d_in_3d(self, view, angle, scale, edge_color,
1742
+ facet_color, opacity, vertex_color, axis):
1743
+ r"""
1744
+ Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
1745
+ according to a projection ``view`` and an angle ``angle``
1746
+ obtained via Jmol through the current state property. ``self`` is
1747
+ assumed to be a 3-polytope in 3-space.
1748
+
1749
+ INPUT:
1750
+
1751
+ - ``view`` -- list (default: [0,0,1]) representing the rotation axis
1752
+ - ``angle`` -- integer angle of rotation in degree from 0 to 360
1753
+ - ``scale`` -- integer specifying the scaling of the tikz picture
1754
+ - ``edge_color`` -- string representing colors which tikz
1755
+ recognizes
1756
+ - ``facet_color`` -- string representing colors which tikz
1757
+ recognizes
1758
+ - ``vertex_color`` -- string representing colors which tikz
1759
+ recognizes
1760
+ - ``opacity`` -- real number between 0 and 1 giving the opacity of
1761
+ the front facets
1762
+ - ``axis`` -- boolean draw the axes at the origin or not
1763
+
1764
+ OUTPUT: :class:`LatexExpr` -- containing the TikZ picture.
1765
+
1766
+ EXAMPLES::
1767
+
1768
+ sage: # needs sage.plot sage.rings.number_field
1769
+ sage: P = polytopes.small_rhombicuboctahedron()
1770
+ sage: Image = P.projection()._tikz_3d_in_3d([3, 7, 5], 100, scale=3,
1771
+ ....: edge_color='blue', facet_color='orange',
1772
+ ....: opacity=0.5, vertex_color='green', axis=True)
1773
+ sage: type(Image)
1774
+ <class 'sage.misc.latex.LatexExpr'>
1775
+ sage: print('\n'.join(Image.splitlines()[:4]))
1776
+ \begin{tikzpicture}%
1777
+ [x={(-0.046385cm, 0.837431cm)},
1778
+ y={(-0.243536cm, 0.519228cm)},
1779
+ z={(0.968782cm, 0.170622cm)},
1780
+ sage: with open('polytope-tikz1.tex', 'w') as f: # not tested
1781
+ ....: _ = f.write(Image)
1782
+
1783
+ ::
1784
+
1785
+ sage: # needs sage.plot
1786
+ sage: Associahedron = Polyhedron(vertices=[[1, 0, 1], [1, 0, 0], [1, 1, 0],
1787
+ ....: [0, 0, -1], [0, 1, 0], [-1, 0, 0],
1788
+ ....: [0, 1, 1], [0, 0, 1], [0, -1, 0]]).polar()
1789
+ sage: ImageAsso = Associahedron.projection().tikz([-15, -755, -655], 116, scale=1,
1790
+ ....: output_type='LatexExpr')
1791
+ sage: print('\n'.join(ImageAsso.splitlines()[12:30]))
1792
+ %% with the command: ._tikz_3d_in_3d and parameters:
1793
+ %% view = [-15, -755, -655]
1794
+ %% angle = 116
1795
+ %% scale = 1
1796
+ %% edge_color = blue!95!black
1797
+ %% facet_color = blue!95!black
1798
+ %% opacity = 0.8
1799
+ %% vertex_color = green
1800
+ %% axis = False
1801
+ %%
1802
+ %% Coordinate of the vertices:
1803
+ %%
1804
+ \coordinate (0.00000, 1.00000, -1.00000) at (0.00000, 1.00000, -1.00000);
1805
+ \coordinate (1.00000, 1.00000, -1.00000) at (1.00000, 1.00000, -1.00000);
1806
+ \coordinate (1.00000, 1.00000, 1.00000) at (1.00000, 1.00000, 1.00000);
1807
+ \coordinate (1.00000, -1.00000, 1.00000) at (1.00000, -1.00000, 1.00000);
1808
+ \coordinate (1.00000, -1.00000, 0.00000) at (1.00000, -1.00000, 0.00000);
1809
+ \coordinate (1.00000, 0.00000, -1.00000) at (1.00000, 0.00000, -1.00000);
1810
+ """
1811
+ from sage.rings.real_double import RDF
1812
+
1813
+ view_vector = vector(RDF, view)
1814
+ rot = rotate_arbitrary(view_vector, -(angle/360)*2*pi)
1815
+ rotation_matrix = rot[:2].transpose()
1816
+ proj_vector = (rot**(-1))*vector(RDF, [0, 0, 1])
1817
+
1818
+ # First compute the back and front vertices and facets
1819
+ front_facets, back_facets, front_vertices, back_vertices = self._front_back_facets(proj_vector)
1820
+
1821
+ # Creates the nodes, coordinate and tag for every vertex of the polytope.
1822
+ # The tag is used to draw the front facets later on.
1823
+ dict_drawing = {}
1824
+ back_part = ''
1825
+ front_part = ''
1826
+
1827
+ for vert in self.points:
1828
+ v = self.coords[vert]
1829
+ v_vect = str(['%.5f' % i for i in v]).replace('\'', '')
1830
+ v_vect = v_vect.replace('[', '(')
1831
+ v_vect = v_vect.replace(']', ')')
1832
+ tag = '%s' % v_vect
1833
+ node = "\\node[%s] at %s {};\n" % ('vertex', tag)
1834
+ coord = '\\coordinate %s at %s;\n' % (tag, tag)
1835
+ dict_drawing[vert] = node, coord, tag
1836
+
1837
+ # Separate the edges between back and front
1838
+ facets = self.face_inequalities
1839
+ for index1, index2 in self.lines:
1840
+ # v1 = self.coords[index1]
1841
+ # v2 = self.coords[index2]
1842
+
1843
+ H_v1 = set(self.parent_polyhedron.Vrepresentation(index1).incident())
1844
+ H_v2 = set(self.parent_polyhedron.Vrepresentation(index2).incident())
1845
+ H_v12 = [h for h in H_v1.intersection(H_v2) if facets.index(h) in back_facets]
1846
+
1847
+ # The back edge has to be between two vertices in the Back
1848
+ # AND such that the 2 facets touching them are in the Back
1849
+ if index1 in back_vertices and index2 in back_vertices and len(H_v12) == 2:
1850
+ back_part += "\\draw[%s,back] %s -- %s;\n" % ('edge',
1851
+ dict_drawing[index1][2],
1852
+ dict_drawing[index2][2])
1853
+ else:
1854
+ front_part += "\\draw[%s] %s -- %s;\n" % ('edge',
1855
+ dict_drawing[index1][2],
1856
+ dict_drawing[index2][2])
1857
+
1858
+ # Start to write the output
1859
+ tikz_pic = ''
1860
+ tikz_pic += '\\begin{tikzpicture}%\n'
1861
+ tikz_pic += '\t[x={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[0][0]),
1862
+ RDF(rotation_matrix[0][1]))
1863
+ tikz_pic += '\ty={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[1][0]),
1864
+ RDF(rotation_matrix[1][1]))
1865
+ tikz_pic += '\tz={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[2][0]),
1866
+ RDF(rotation_matrix[2][1]))
1867
+ tikz_pic += '\tscale=%f,\n' % scale
1868
+ tikz_pic += '\tback/.style={loosely dotted, thin},\n'
1869
+ tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color
1870
+ tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color, opacity)
1871
+ tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color
1872
+ tikz_pic += 'fill=%s!75!black,thick}]\n%%\n%%\n' % vertex_color
1873
+
1874
+ # Gives the reproduction information
1875
+ from sage.env import SAGE_VERSION
1876
+ tikz_pic += "%% This TikZ-picture was produced with Sagemath version {}\n".format(SAGE_VERSION)
1877
+ tikz_pic += "%% with the command: ._tikz_3d_in_3d and parameters:\n"
1878
+ tikz_pic += "%% view = {}\n".format(view)
1879
+ tikz_pic += "%% angle = {}\n".format(angle)
1880
+ tikz_pic += "%% scale = {}\n".format(scale)
1881
+ tikz_pic += "%% edge_color = {}\n".format(edge_color)
1882
+ tikz_pic += "%% facet_color = {}\n".format(facet_color)
1883
+ tikz_pic += "%% opacity = {}\n".format(opacity)
1884
+ tikz_pic += "%% vertex_color = {}\n".format(vertex_color)
1885
+ tikz_pic += "%% axis = {}\n%%\n".format(axis)
1886
+
1887
+ # Draws the axes if True
1888
+ if axis:
1889
+ tikz_pic += '%% Drawing the axes\n'
1890
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
1891
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'
1892
+ tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'
1893
+
1894
+ # Create the coordinate of the vertices
1895
+ tikz_pic += '%% Coordinate of the vertices:\n%%\n'
1896
+ for v in dict_drawing:
1897
+ tikz_pic += dict_drawing[v][1]
1898
+
1899
+ # Draw the edges in the back
1900
+ tikz_pic += '%%\n%%\n%% Drawing edges in the back\n%%\n'
1901
+ tikz_pic += back_part
1902
+
1903
+ # Draw the vertices on top of the back-edges
1904
+ tikz_pic += '%%\n%%\n%% Drawing vertices in the back\n%%\n'
1905
+ for v in back_vertices:
1906
+ if v not in front_vertices and v in dict_drawing:
1907
+ tikz_pic += dict_drawing[v][0]
1908
+
1909
+ # Draw the facets in the front by going in cycles for every facet.
1910
+ tikz_pic += '%%\n%%\n%% Drawing the facets\n%%\n'
1911
+ vertices = self.parent_polyhedron.Vrep_generator()
1912
+ vertex_to_index = {v: i for i, v in enumerate(vertices)}
1913
+ for index_facet in front_facets:
1914
+ cyclic_vert = cyclic_sort_vertices_2d(list(facets[index_facet].incident()))
1915
+ cyclic_indices = [vertex_to_index[v] for v in cyclic_vert]
1916
+ tikz_pic += '\\fill[facet] '
1917
+ for v in cyclic_indices:
1918
+ if v in dict_drawing:
1919
+ tikz_pic += '%s -- ' % dict_drawing[v][2]
1920
+ tikz_pic += 'cycle {};\n'
1921
+
1922
+ # Draw the edges in the front
1923
+ tikz_pic += '%%\n%%\n%% Drawing edges in the front\n%%\n'
1924
+ tikz_pic += front_part
1925
+
1926
+ # Finally, the vertices in front are drawn on top of everything.
1927
+ tikz_pic += '%%\n%%\n%% Drawing the vertices in the front\n%%\n'
1928
+ for v in self.points:
1929
+ if v in front_vertices:
1930
+ if v in dict_drawing:
1931
+ tikz_pic += dict_drawing[v][0]
1932
+ tikz_pic += '%%\n%%\n\\end{tikzpicture}'
1933
+ return LatexExpr(tikz_pic)
1934
+
1935
+ def _front_back_facets(self, projection_vector):
1936
+ r"""
1937
+ Return the front/back vertices/facets of the projected polyhedron
1938
+ with respect to the projection vector.
1939
+
1940
+ INPUT:
1941
+
1942
+ - ``projection_vector`` -- vector
1943
+
1944
+ EXAMPLES::
1945
+
1946
+ sage: # needs sage.plot sage.rings.number_field
1947
+ sage: P = polytopes.small_rhombicuboctahedron()
1948
+ sage: from sage.geometry.polyhedron.plot import Projection
1949
+ sage: proj = Projection(P)
1950
+ sage: v = (-0.544571767341018, 0.8192019648731899, 0.17986030958214505)
1951
+ sage: v = vector(RDF, v)
1952
+ sage: proj._front_back_facets(v)
1953
+ ([0, 2, 4, 5, 8, 9, 10, 17, 18, 19, 20, 22, 24],
1954
+ [1, 3, 6, 7, 11, 12, 13, 14, 15, 16, 21, 23, 25],
1955
+ [2, 3, 14, 15, 0, 12, 1, 4, 13, 16, 6, 7, 18, 19, 20, 21, 23],
1956
+ [4, 9, 16, 21, 3, 5, 15, 17, 10, 22, 2, 6, 8, 7, 11, 20, 23])
1957
+ """
1958
+ facet_ineqs = self.face_inequalities
1959
+ front_facets = []
1960
+ back_facets = []
1961
+ for index_facet,f in enumerate(facet_ineqs):
1962
+ A = f.A()
1963
+ if A * projection_vector < 0:
1964
+ front_facets.append(index_facet)
1965
+ else:
1966
+ back_facets.append(index_facet)
1967
+
1968
+ front_vertices = []
1969
+ for index_facet in front_facets:
1970
+ f_ineq = facet_ineqs[index_facet]
1971
+ A = f_ineq.A()
1972
+ b = f_ineq.b()
1973
+ for v in self.points:
1974
+ if A * self.coords[v] + b < 0.0005 and v not in front_vertices:
1975
+ front_vertices.append(v)
1976
+
1977
+ back_vertices = []
1978
+ for index_facet in back_facets:
1979
+ f_ineq = facet_ineqs[index_facet]
1980
+ A = f_ineq.A()
1981
+ b = f_ineq.b()
1982
+ for v in self.points:
1983
+ if A * self.coords[v] + b < 0.0005 and v not in back_vertices:
1984
+ back_vertices.append(v)
1985
+
1986
+ return front_facets, back_facets, front_vertices, back_vertices