passagemath-polyhedra 10.6.37__cp314-cp314-musllinux_1_2_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.
- passagemath_polyhedra/__init__.py +3 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA +367 -0
- passagemath_polyhedra-10.6.37.dist-info/METADATA.bak +369 -0
- passagemath_polyhedra-10.6.37.dist-info/RECORD +209 -0
- passagemath_polyhedra-10.6.37.dist-info/WHEEL +5 -0
- passagemath_polyhedra-10.6.37.dist-info/top_level.txt +3 -0
- passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
- passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
- passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
- passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
- sage/all__sagemath_polyhedra.py +50 -0
- sage/game_theory/all.py +8 -0
- sage/game_theory/catalog.py +6 -0
- sage/game_theory/catalog_normal_form_games.py +923 -0
- sage/game_theory/cooperative_game.py +844 -0
- sage/game_theory/matching_game.py +1181 -0
- sage/game_theory/normal_form_game.py +2697 -0
- sage/game_theory/parser.py +275 -0
- sage/geometry/all__sagemath_polyhedra.py +22 -0
- sage/geometry/cone.py +6940 -0
- sage/geometry/cone_catalog.py +847 -0
- sage/geometry/cone_critical_angles.py +1027 -0
- sage/geometry/convex_set.py +1119 -0
- sage/geometry/fan.py +3743 -0
- sage/geometry/fan_isomorphism.py +389 -0
- sage/geometry/fan_morphism.py +1884 -0
- sage/geometry/hasse_diagram.py +202 -0
- sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
- sage/geometry/hyperplane_arrangement/all.py +1 -0
- sage/geometry/hyperplane_arrangement/arrangement.py +3905 -0
- sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
- sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
- sage/geometry/hyperplane_arrangement/library.py +825 -0
- sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
- sage/geometry/hyperplane_arrangement/plot.py +520 -0
- sage/geometry/integral_points.py +35 -0
- sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/integral_points_generic_dense.pyx +7 -0
- sage/geometry/lattice_polytope.py +5894 -0
- sage/geometry/linear_expression.py +773 -0
- sage/geometry/newton_polygon.py +767 -0
- sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/point_collection.pyx +1008 -0
- sage/geometry/polyhedral_complex.py +2616 -0
- sage/geometry/polyhedron/all.py +8 -0
- sage/geometry/polyhedron/backend_cdd.py +460 -0
- sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
- sage/geometry/polyhedron/backend_field.py +347 -0
- sage/geometry/polyhedron/backend_normaliz.py +2503 -0
- sage/geometry/polyhedron/backend_number_field.py +168 -0
- sage/geometry/polyhedron/backend_polymake.py +765 -0
- sage/geometry/polyhedron/backend_ppl.py +582 -0
- sage/geometry/polyhedron/base.py +1206 -0
- sage/geometry/polyhedron/base0.py +1444 -0
- sage/geometry/polyhedron/base1.py +886 -0
- sage/geometry/polyhedron/base2.py +812 -0
- sage/geometry/polyhedron/base3.py +1845 -0
- sage/geometry/polyhedron/base4.py +1262 -0
- sage/geometry/polyhedron/base5.py +2700 -0
- sage/geometry/polyhedron/base6.py +1741 -0
- sage/geometry/polyhedron/base7.py +997 -0
- sage/geometry/polyhedron/base_QQ.py +1258 -0
- sage/geometry/polyhedron/base_RDF.py +98 -0
- sage/geometry/polyhedron/base_ZZ.py +934 -0
- sage/geometry/polyhedron/base_mutable.py +215 -0
- sage/geometry/polyhedron/base_number_field.py +122 -0
- sage/geometry/polyhedron/cdd_file_format.py +155 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
- sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
- sage/geometry/polyhedron/constructor.py +773 -0
- sage/geometry/polyhedron/double_description.py +753 -0
- sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
- sage/geometry/polyhedron/face.py +1060 -0
- sage/geometry/polyhedron/generating_function.py +1810 -0
- sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
- sage/geometry/polyhedron/library.py +3502 -0
- sage/geometry/polyhedron/misc.py +121 -0
- sage/geometry/polyhedron/modules/all.py +1 -0
- sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
- sage/geometry/polyhedron/palp_database.py +447 -0
- sage/geometry/polyhedron/parent.py +1279 -0
- sage/geometry/polyhedron/plot.py +1986 -0
- sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
- sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
- sage/geometry/polyhedron/representation.py +1723 -0
- sage/geometry/pseudolines.py +515 -0
- sage/geometry/relative_interior.py +445 -0
- sage/geometry/toric_plotter.py +1103 -0
- sage/geometry/triangulation/all.py +2 -0
- sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
- sage/geometry/triangulation/base.pyx +963 -0
- sage/geometry/triangulation/data.h +147 -0
- sage/geometry/triangulation/data.pxd +4 -0
- sage/geometry/triangulation/element.py +914 -0
- sage/geometry/triangulation/functions.h +10 -0
- sage/geometry/triangulation/functions.pxd +4 -0
- sage/geometry/triangulation/point_configuration.py +2256 -0
- sage/geometry/triangulation/triangulations.h +49 -0
- sage/geometry/triangulation/triangulations.pxd +7 -0
- sage/geometry/voronoi_diagram.py +319 -0
- sage/interfaces/all__sagemath_polyhedra.py +1 -0
- sage/interfaces/polymake.py +2028 -0
- sage/numerical/all.py +13 -0
- sage/numerical/all__sagemath_polyhedra.py +11 -0
- sage/numerical/backends/all.py +1 -0
- sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
- sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_backend.pyx +1006 -0
- sage/numerical/backends/cvxopt_backend_test.py +19 -0
- sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
- sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/cvxpy_backend.pxd +41 -0
- sage/numerical/backends/cvxpy_backend.pyx +934 -0
- sage/numerical/backends/cvxpy_backend_test.py +13 -0
- sage/numerical/backends/generic_backend_test.py +24 -0
- sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/interactivelp_backend.pxd +36 -0
- sage/numerical/backends/interactivelp_backend.pyx +1231 -0
- sage/numerical/backends/interactivelp_backend_test.py +12 -0
- sage/numerical/backends/logging_backend.py +391 -0
- sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
- sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
- sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/ppl_backend.pyx +1126 -0
- sage/numerical/backends/ppl_backend_test.py +13 -0
- sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/backends/scip_backend.pxd +22 -0
- sage/numerical/backends/scip_backend.pyx +1289 -0
- sage/numerical/backends/scip_backend_test.py +13 -0
- sage/numerical/interactive_simplex_method.py +5338 -0
- sage/numerical/knapsack.py +665 -0
- sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_functions.pxd +31 -0
- sage/numerical/linear_functions.pyx +1648 -0
- sage/numerical/linear_tensor.py +470 -0
- sage/numerical/linear_tensor_constraints.py +448 -0
- sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/linear_tensor_element.pxd +6 -0
- sage/numerical/linear_tensor_element.pyx +459 -0
- sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/mip.pxd +40 -0
- sage/numerical/mip.pyx +3667 -0
- sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
- sage/numerical/sdp.pxd +39 -0
- sage/numerical/sdp.pyx +1433 -0
- sage/rings/all__sagemath_polyhedra.py +3 -0
- sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
- sage/rings/polynomial/omega.py +982 -0
- sage/schemes/all__sagemath_polyhedra.py +2 -0
- sage/schemes/toric/all.py +10 -0
- sage/schemes/toric/chow_group.py +1248 -0
- sage/schemes/toric/divisor.py +2082 -0
- sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
- sage/schemes/toric/divisor_class.pyx +322 -0
- sage/schemes/toric/fano_variety.py +1606 -0
- sage/schemes/toric/homset.py +650 -0
- sage/schemes/toric/ideal.py +451 -0
- sage/schemes/toric/library.py +1322 -0
- sage/schemes/toric/morphism.py +1958 -0
- sage/schemes/toric/points.py +1032 -0
- sage/schemes/toric/sheaf/all.py +1 -0
- sage/schemes/toric/sheaf/constructor.py +302 -0
- sage/schemes/toric/sheaf/klyachko.py +921 -0
- sage/schemes/toric/toric_subscheme.py +905 -0
- sage/schemes/toric/variety.py +3460 -0
- sage/schemes/toric/weierstrass.py +1078 -0
- sage/schemes/toric/weierstrass_covering.py +457 -0
- sage/schemes/toric/weierstrass_higher.py +288 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
- sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
- sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
- sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
- 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
|