Path: blob/master/src/sage/geometry/polyhedron/plot.py
8817 views
"""1Functions for plotting polyhedra2"""34########################################################################5# Copyright (C) 2008 Marshall Hampton <[email protected]>6# Copyright (C) 2011 Volker Braun <[email protected]>7#8# Distributed under the terms of the GNU General Public License (GPL)9#10# http://www.gnu.org/licenses/11########################################################################121314from sage.rings.all import RDF15from sage.structure.sage_object import SageObject16from sage.modules.free_module_element import vector17from sage.matrix.constructor import matrix, identity_matrix18from sage.misc.functional import norm19from sage.misc.latex import LatexExpr20from sage.symbolic.constants import pi21from sage.structure.sequence import Sequence2223from sage.plot.all import point2d, line2d, arrow, polygon2d24from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d25from sage.plot.plot3d.transform import rotate_arbitrary2627from base import is_Polyhedron28293031#############################################################32def render_2d(projection, point_opts={}, line_opts={}, polygon_opts={}):33"""34Return 2d rendering of the projection of a polyhedron into352-dimensional ambient space.3637EXAMPLES::3839sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]])40sage: q1 = p1.projection()41sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]])42sage: q2 = p2.projection()43sage: p3 = Polyhedron(vertices=[[1,2]])44sage: q3 = p3.projection()45sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]])46sage: q4 = p4.projection()47sage: q1.show() + q2.show() + q3.show() + q4.show()48sage: from sage.geometry.polyhedron.plot import render_2d49sage: q = render_2d(p1.projection())50sage: q._objects51[Point set defined by 1 point(s),52Arrow from (1.0,1.0) to (2.0,2.0),53Polygon defined by 3 points]54"""55if is_Polyhedron(projection):56projection = Projection(projection)57from sage.plot.graphics import Graphics58plt = Graphics()59if isinstance(point_opts, dict):60point_opts.setdefault('zorder', 2)61point_opts.setdefault('pointsize', 10)62plt += projection.render_points_2d(**point_opts)63if isinstance(line_opts, dict):64line_opts.setdefault('zorder', 1)65plt += projection.render_outline_2d(**line_opts)66if isinstance(polygon_opts, dict):67polygon_opts.setdefault('zorder', 0)68plt += projection.render_fill_2d(**polygon_opts)69return plt707172def render_3d(projection, point_opts={}, line_opts={}, polygon_opts={}):73"""74Return 3d rendering of a polyhedron projected into753-dimensional ambient space.7677.. NOTE::7879This method, ``render_3d``, is used in the ``show()``80method of a polyhedron if it is in 3 dimensions.8182EXAMPLES::8384sage: p1 = Polyhedron(vertices=[[1,1,1]], rays=[[1,1,1]])85sage: p2 = Polyhedron(vertices=[[2,0,0], [0,2,0], [0,0,2]])86sage: p3 = Polyhedron(vertices=[[1,0,0], [0,1,0], [0,0,1]], rays=[[-1,-1,-1]])87sage: p1.projection().show() + p2.projection().show() + p3.projection().show()8889It correctly handles various degenerate cases::9091sage: Polyhedron(lines=[[1,0,0],[0,1,0],[0,0,1]]).show() # whole space92sage: Polyhedron(vertices=[[1,1,1]], rays=[[1,0,0]], lines=[[0,1,0],[0,0,1]]).show() # half space93sage: Polyhedron(vertices=[[1,1,1]], lines=[[0,1,0],[0,0,1]]).show() # R^2 in R^394sage: Polyhedron(rays=[[0,1,0],[0,0,1]], lines=[[1,0,0]]).show() # quadrant wedge in R^295sage: Polyhedron(rays=[[0,1,0]], lines=[[1,0,0]]).show() # upper half plane in R^396sage: Polyhedron(lines=[[1,0,0]]).show() # R^1 in R^297sage: Polyhedron(rays=[[0,1,0]]).show() # Half-line in R^398sage: Polyhedron(vertices=[[1,1,1]]).show() # point in R^399"""100if is_Polyhedron(projection):101projection = Projection(projection)102from sage.plot.plot3d.base import Graphics3d103plt = Graphics3d()104if isinstance(point_opts, dict):105point_opts.setdefault('width', 3)106plt += projection.render_vertices_3d(**point_opts)107if isinstance(line_opts, dict):108line_opts.setdefault('width', 3)109plt += projection.render_wireframe_3d(**line_opts)110if isinstance(polygon_opts, dict):111plt += projection.render_solid_3d(**polygon_opts)112return plt113114def render_4d(polyhedron, point_opts={}, line_opts={}, polygon_opts={}, projection_direction=None):115"""116Return a 3d rendering of the Schlegel projection of a 4d117polyhedron projected into 3-dimensional space.118119.. NOTE::120121The ``show()`` method of ``Polyhedron()`` uses this to draw itself122if the ambient dimension is 4.123124INPUT:125126- ``polyhedron`` -- A127:mod:`~sage.geometry.polyhedron.constructor.Polyhedron` object.128129- ``point_opts``, ``line_opts``, ``polygon_opts`` -- dictionaries130of plot keywords or ``False`` to disable.131132- ``projection_direction`` -- list/tuple/iterable of coordinates133or ``None`` (default). Sets the projetion direction of the134Schlegel projection. If it is not given, the center of a facet135is used.136137EXAMPLES::138139sage: poly = polytopes.twenty_four_cell()140sage: poly141A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 24 vertices142sage: poly.show()143sage: poly.show(projection_direction=[2,5,11,17])144sage: type( poly.show() )145<class 'sage.plot.plot3d.base.Graphics3dGroup'>146147TESTS::148149sage: from sage.geometry.polyhedron.plot import render_4d150sage: p = polytopes.n_cube(4)151sage: q = render_4d(p)152sage: tach_str = q.tachyon()153sage: tach_str.count('FCylinder')15432155"""156if projection_direction is None:157for ineq in polyhedron.inequality_generator():158center = [v() for v in ineq.incident() if v.is_vertex()]159center = sum(center) / len(center)160if not center.is_zero():161projection_direction = center162break163projection_3d = Projection(polyhedron).schlegel(projection_direction)164return render_3d(projection_3d, point_opts, line_opts, polygon_opts)165166167168#############################################################169def cyclic_sort_vertices_2d(Vlist):170"""171Return the vertices/rays in cyclic order if possible.172173.. NOTE::174175This works if and only if each vertex/ray is adjacent to exactly176two others. For example, any 2-dimensional polyhedron satisfies177this.178179See180:meth:`~sage.geometry.polyhedron.base.Polyhedron_base.vertex_adjacency_matrix`181for a discussion of "adjacent".182183EXAMPLES::184185sage: from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d186sage: square = Polyhedron([[1,0],[-1,0],[0,1],[0,-1]])187sage: vertices = [v for v in square.vertex_generator()]188sage: vertices189[A vertex at (-1, 0),190A vertex at (0, -1),191A vertex at (0, 1),192A vertex at (1, 0)]193sage: cyclic_sort_vertices_2d(vertices)194[A vertex at (1, 0),195A vertex at (0, -1),196A vertex at (-1, 0),197A vertex at (0, 1)]198199Rays are allowed, too::200201sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (2, 0), (3, 0), (4, 1)], rays=[(0,1)])202sage: P.adjacency_matrix()203[0 1 0 1 0]204[1 0 1 0 0]205[0 1 0 0 1]206[1 0 0 0 1]207[0 0 1 1 0]208sage: cyclic_sort_vertices_2d(P.Vrepresentation())209[A vertex at (3, 0),210A vertex at (1, 0),211A vertex at (0, 1),212A ray in the direction (0, 1),213A vertex at (4, 1)]214215sage: P = Polyhedron(vertices=[(0, 1), (1, 0), (2, 0), (3, 0), (4, 1)], rays=[(0,1), (1,1)])216sage: P.adjacency_matrix()217[0 1 0 0 0]218[1 0 1 0 0]219[0 1 0 0 1]220[0 0 0 0 1]221[0 0 1 1 0]222sage: cyclic_sort_vertices_2d(P.Vrepresentation())223[A ray in the direction (1, 1),224A vertex at (3, 0),225A vertex at (1, 0),226A vertex at (0, 1),227A ray in the direction (0, 1)]228229sage: P = Polyhedron(vertices=[(1,2)], rays=[(0,1)], lines=[(1,0)])230sage: P.adjacency_matrix()231[0 0 1]232[0 0 0]233[1 0 0]234sage: cyclic_sort_vertices_2d(P.Vrepresentation())235[A vertex at (0, 2),236A line in the direction (1, 0),237A ray in the direction (0, 1)]238"""239if len(Vlist)==0: return Vlist240Vlist = list(Vlist)241result = []242adjacency_matrix = Vlist[0].polyhedron().vertex_adjacency_matrix()243244# Any object in Vlist has 0,1, or 2 adjacencies. Break into connected chains:245chain = [ Vlist.pop() ]246while len(Vlist)>0:247first_index = chain[0].index()248last_index = chain[-1].index()249for v in Vlist:250v_index = v.index()251if adjacency_matrix[last_index, v_index] == 1:252chain = chain + [v]253Vlist.remove(v)254break;255if adjacency_matrix[first_index, v.index()] == 1:256chain = [v] + chain257Vlist.remove(v)258break;259else:260result += chain261chain = [ Vlist.pop() ]262result += chain263return result264265266267268#########################################################################269def projection_func_identity(x):270"""271The identity projection.272273EXAMPLES::274275sage: from sage.geometry.polyhedron.plot import projection_func_identity276sage: projection_func_identity((1,2,3))277[1, 2, 3]278"""279return list(x)280281282283class ProjectionFuncStereographic():284"""285The stereographic (or perspective) projection.286287EXAMPLES::288289sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic290sage: cube = polytopes.n_cube(3).vertices()291sage: proj = ProjectionFuncStereographic([1.2, 3.4, 5.6])292sage: ppoints = [proj(vector(x)) for x in cube]293sage: ppoints[0]294(-0.0486511..., 0.0859565...)295"""296def __init__(self, projection_point):297"""298Create a stereographic projection function.299300INPUT:301302- ``projection_point`` -- a list of coordinates in the303appropriate dimension, which is the point projected from.304305EXAMPLES::306307sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic308sage: proj = ProjectionFuncStereographic([1.0,1.0])309sage: proj.__init__([1.0,1.0])310sage: proj.house311[-0.7071067811... 0.7071067811...]312[ 0.7071067811... 0.7071067811...]313sage: TestSuite(proj).run(skip='_test_pickling')314"""315self.projection_point = vector(projection_point)316self.dim = self.projection_point.degree()317318pproj = vector(RDF,self.projection_point)319self.psize = norm(pproj)320if (self.psize).is_zero():321raise ValueError, "projection direction must be a non-zero vector."322v = vector(RDF, [0.0]*(self.dim-1) + [self.psize]) - pproj323polediff = matrix(RDF,v).transpose()324denom = RDF((polediff.transpose()*polediff)[0][0])325if denom.is_zero():326self.house = identity_matrix(RDF,self.dim)327else:328self.house = identity_matrix(RDF,self.dim) \329- 2*polediff*polediff.transpose()/denom # Householder reflector330331def __call__(self, x):332"""333Action of the stereographic projection.334335INPUT:336337- ``x`` -- a vector or anything convertible to a vector.338339OUTPUT:340341First reflects ``x`` with a Householder reflection which takes342the projection point to ``(0,...,0,self.psize)`` where343``psize`` is the length of the projection point, and then344dilates by ``1/(zdiff)`` where ``zdiff`` is the difference345between the last coordinate of ``x`` and ``psize``.346347EXAMPLES::348349sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic350sage: proj = ProjectionFuncStereographic([1.0,1.0])351sage: proj.__call__(vector([1,2]))352(-1.0)353sage: proj = ProjectionFuncStereographic([2.0,1.0])354sage: proj.__call__(vector([1,2]))355(3.0)356sage: proj = ProjectionFuncStereographic([0,0,2])357sage: proj.__call__(vector([0,0,1]))358(0.0, 0.0)359sage: proj.__call__(vector([1,0,0]))360(0.5, 0.0)361"""362img = self.house * x363denom = self.psize-img[self.dim-1]364if denom.is_zero():365raise ValueError, 'Point cannot coincide with ' \366'coordinate singularity at ' + repr(x)367return vector(RDF, [img[i]/denom for i in range(self.dim-1)])368369370class ProjectionFuncSchlegel():371"""372The Schlegel projection from the given input point.373374EXAMPLES::375376sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel377sage: proj = ProjectionFuncSchlegel([2,2,2])378sage: proj(vector([1.1,1.1,1.11]))[0]3790.0302...380"""381def __init__(self, projection_direction, height = 1.1):382"""383Initializes the projection.384385EXAMPLES::386387sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel388sage: proj = ProjectionFuncSchlegel([2,2,2])389sage: proj.__init__([2,2,2])390sage: proj(vector([1.1,1.1,1.11]))[0]3910.0302...392sage: TestSuite(proj).run(skip='_test_pickling')393"""394self.projection_dir = vector(RDF, projection_direction)395if norm(self.projection_dir).is_zero():396raise ValueError, "projection direction must be a non-zero vector."397self.dim = self.projection_dir.degree()398spcenter = height * self.projection_dir/norm(self.projection_dir)399self.height = height400v = vector(RDF, [0.0]*(self.dim-1) + [self.height]) - spcenter401polediff = matrix(RDF,v).transpose()402denom = (polediff.transpose()*polediff)[0][0]403if denom.is_zero():404self.house = identity_matrix(RDF,self.dim)405else:406self.house = identity_matrix(RDF,self.dim) \407- 2*polediff*polediff.transpose()/denom #Householder reflector408409def __call__(self, x):410"""411Apply the projection to a vector.412413- ``x`` -- a vector or anything convertible to a vector.414415EXAMPLES::416417sage: from sage.geometry.polyhedron.plot import ProjectionFuncSchlegel418sage: proj = ProjectionFuncSchlegel([2,2,2])419sage: proj.__call__([1,2,3])420(0.56162854..., 2.09602626...)421"""422v = vector(RDF,x)423if v.is_zero():424raise ValueError, "The origin must not be a vertex."425v = v/norm(v) # normalize vertices to unit sphere426v = self.house*v # reflect so self.projection_dir is at "north pole"427denom = self.height-v[self.dim-1]428if denom.is_zero():429raise ValueError, 'Point cannot coincide with ' \430'coordinate singularity at ' + repr(x)431return vector(RDF, [ v[i]/denom for i in range(self.dim-1) ])432433434435#########################################################################436class Projection(SageObject):437"""438The projection of a :class:`Polyhedron`.439440This class keeps track of the necessary data to plot the input441polyhedron.442"""443444def __init__(self, polyhedron, proj=projection_func_identity):445"""446Initialize the projection of a Polyhedron() object.447448INPUT:449450- ``polyhedron`` -- a ``Polyhedron()`` object451452- ``proj`` -- a projection function for the points453454.. NOTE::455456Once initialized, the polyhedral data is fixed. However, the457projection can be changed later on.458459EXAMPLES::460461sage: p = polytopes.icosahedron()462sage: from sage.geometry.polyhedron.plot import Projection463sage: Projection(p)464The projection of a polyhedron into 3 dimensions465sage: def pr_12(x): return [x[1],x[2]]466sage: Projection(p, pr_12)467The projection of a polyhedron into 2 dimensions468sage: Projection(p, lambda x: [x[1],x[2]] ) # another way of doing the same projection469The projection of a polyhedron into 2 dimensions470sage: _.show() # plot of the projected icosahedron in 2d471sage: proj = Projection(p)472sage: proj.stereographic([1,2,3])473The projection of a polyhedron into 2 dimensions474sage: proj.show()475sage: TestSuite(proj).run(skip='_test_pickling')476"""477self.parent_polyhedron = polyhedron478self.coords = Sequence([])479self.points = Sequence([])480self.lines = Sequence([])481self.arrows = Sequence([])482self.polygons = Sequence([])483self.polyhedron_ambient_dim = polyhedron.ambient_dim()484485if polyhedron.ambient_dim() == 2:486self._init_from_2d(polyhedron)487elif polyhedron.ambient_dim() == 3:488self._init_from_3d(polyhedron)489else:490self._init_points(polyhedron)491self._init_lines_arrows(polyhedron)492493self.coords.set_immutable()494self.points.set_immutable()495self.lines.set_immutable()496self.arrows.set_immutable()497self.polygons.set_immutable()498499self.__call__(proj)500501502def _repr_(self):503"""504Return a string describing the projection.505506EXAMPLES::507508sage: p = polytopes.n_cube(3)509sage: from sage.geometry.polyhedron.plot import Projection510sage: proj = Projection(p)511sage: print proj._repr_()512The projection of a polyhedron into 3 dimensions513"""514s = 'The projection of a polyhedron into ' + \515repr(self.dimension) + ' dimensions'516return s517518519def __call__(self, proj=projection_func_identity):520"""521Apply a projection.522523EXAMPLES::524525sage: p = polytopes.icosahedron()526sage: from sage.geometry.polyhedron.plot import Projection527sage: pproj = Projection(p)528sage: from sage.geometry.polyhedron.plot import ProjectionFuncStereographic529sage: pproj_stereo = pproj.__call__(proj = ProjectionFuncStereographic([1,2,3]))530sage: pproj_stereo.polygons[0]531[10, 4, 6]532"""533self.transformed_coords = \534Sequence([proj(p) for p in self.coords])535self._init_dimension()536return self537538539def identity(self):540"""541Return the identity projection of the polyhedron.542543EXAMPLES::544545sage: p = polytopes.icosahedron()546sage: from sage.geometry.polyhedron.plot import Projection547sage: pproj = Projection(p)548sage: ppid = pproj.identity()549sage: ppid.dimension5503551"""552return self.__call__(projection_func_identity)553554555def stereographic(self, projection_point=None):556r"""557Return the stereographic projection.558559INPUT:560561- ``projection_point`` - The projection point. This must be562distinct from the polyhedron's vertices. Default is `(1,0,\dots,0)`563564EXAMPLES::565566sage: from sage.geometry.polyhedron.plot import Projection567sage: proj = Projection(polytopes.buckyball()) #long time568sage: proj #long time569The projection of a polyhedron into 3 dimensions570sage: proj.stereographic([5,2,3]).show() #long time571sage: Projection( polytopes.twenty_four_cell() ).stereographic([2,0,0,0])572The projection of a polyhedron into 3 dimensions573"""574if projection_point == None:575projection_point = [1] + [0]*(self.polyhedron_ambient_dim-1)576return self.__call__(ProjectionFuncStereographic(projection_point))577578579def schlegel(self, projection_direction=None, height = 1.1):580"""581Return the Schlegel projection.582583The vertices are normalized to the unit sphere, and584stereographically projected from a point slightly outside of585the sphere.586587INPUT:588589- ``projection_direction`` - The direction of the Schlegel590projection. By default, the vector consisting of the first n591primes is chosen.592593EXAMPLES::594595sage: cube4 = polytopes.n_cube(4)596sage: from sage.geometry.polyhedron.plot import Projection597sage: Projection(cube4).schlegel([1,0,0,0])598The projection of a polyhedron into 3 dimensions599sage: _.show()600601TESTS::602603sage: Projection(cube4).schlegel()604The projection of a polyhedron into 3 dimensions605606"""607if projection_direction == None:608for poly in self.polygons:609center = sum([self.coords[i] for i in poly]) / len(poly)610print center, "\n"611if not center.is_zero():612projection_direction = center613break614if projection_direction == None:615from sage.rings.arith import primes_first_n616projection_direction = primes_first_n(self.polyhedron_ambient_dim)617return self.__call__(ProjectionFuncSchlegel(projection_direction, height = height))618619620def coord_index_of(self, v):621"""622Convert a coordinate vector to its internal index.623624EXAMPLES::625626sage: p = polytopes.n_cube(3)627sage: proj = p.projection()628sage: proj.coord_index_of(vector((1,1,1)))6297630"""631try:632return self.coords.index(v)633except ValueError:634self.coords.append(v)635return len(self.coords)-1636637638def coord_indices_of(self, v_list):639"""640Convert list of coordinate vectors to the corresponding list641of internal indices.642643EXAMPLES::644645sage: p = polytopes.n_cube(3)646sage: proj = p.projection()647sage: proj.coord_indices_of([vector((1,1,1)),vector((1,-1,1))])648[7, 5]649"""650return [self.coord_index_of(v) for v in v_list]651652653def coordinates_of(self, coord_index_list):654"""655Given a list of indices, return the projected coordinates.656657EXAMPLES::658659sage: p = polytopes.n_simplex(4).projection()660sage: p.coordinates_of([1])661[[0, -81649/100000, 7217/25000, 22361/100000]]662"""663return [self.transformed_coords[i] for i in coord_index_list]664665666def _init_dimension(self):667"""668Internal function: Initialize from 2d polyhedron. Must always669be called after a coordinate projection.670671TESTS::672673sage: from sage.geometry.polyhedron.plot import Projection, render_2d674sage: p = polytopes.n_simplex(2).projection()675sage: test = p._init_dimension()676sage: p.show.__doc__ == render_2d.__doc__677True678"""679self.dimension = len(self.transformed_coords[0])680681if self.dimension == 2:682self.show = lambda **kwds: render_2d(self,**kwds)683self.show.__doc__ = render_2d.__doc__684elif self.dimension == 3:685self.show = lambda **kwds: render_3d(self,**kwds)686self.show.__doc__ = render_3d.__doc__687else:688try:689del self.show690except AttributeError:691pass692693694def _init_from_2d(self, polyhedron):695"""696Internal function: Initialize from 2d polyhedron.697698TESTS::699700sage: p = Polyhedron(vertices = [[0,0],[0,1],[1,0],[1,1]])701sage: proj = p.projection()702sage: [proj.coordinates_of([i]) for i in proj.points]703[[[0, 0]], [[0, 1]], [[1, 0]], [[1, 1]]]704sage: proj._init_from_2d705<bound method Projection._init_from_2d of The projection706of a polyhedron into 2 dimensions>707"""708assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"709self.dimension = 2710self._init_points(polyhedron)711self._init_lines_arrows(polyhedron)712self._init_area_2d(polyhedron)713714715def _init_from_3d(self, polyhedron):716"""717Internal function: Initialize from 3d polyhedron.718719TESTS::720721sage: p = Polyhedron(vertices = [[0,0,1],[0,1,2],[1,0,3],[1,1,5]])722sage: proj = p.projection()723sage: [proj.coordinates_of([i]) for i in proj.points]724[[[0, 0, 1]], [[0, 1, 2]], [[1, 0, 3]], [[1, 1, 5]]]725sage: proj._init_from_3d726<bound method Projection._init_from_3d of The projection727of a polyhedron into 3 dimensions>728"""729assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"730self.dimension = 3731self._init_points(polyhedron)732self._init_lines_arrows(polyhedron)733self._init_solid_3d(polyhedron)734735736def _init_points(self, polyhedron):737"""738Internal function: Initialize points (works in arbitrary739dimensions).740741TESTS::742743sage: p = polytopes.n_cube(2)744sage: pp = p.projection()745sage: del pp.points746sage: pp.points = Sequence([])747sage: pp._init_points(p)748sage: pp.points749[0, 1, 2, 3]750"""751for v in polyhedron.vertex_generator():752self.points.append( self.coord_index_of(v.vector()) )753754755def _init_lines_arrows(self, polyhedron):756"""757Internal function: Initialize compact and non-compact edges758(works in arbitrary dimensions).759760TESTS::761762sage: p = Polyhedron(ieqs = [[1, 0, 0, 1],[1,1,0,0]])763sage: pp = p.projection()764sage: pp.arrows765[[0, 1], [0, 2]]766sage: del pp.arrows767sage: pp.arrows = Sequence([])768sage: pp._init_lines_arrows(p)769sage: pp.arrows770[[0, 1], [0, 2]]771"""772obj = polyhedron.Vrepresentation()773for i in range(len(obj)):774if not obj[i].is_vertex(): continue775for j in range(len(obj)):776if polyhedron.vertex_adjacency_matrix()[i,j] == 0: continue777if i<j and obj[j].is_vertex():778l = [obj[i].vector(), obj[j].vector()]779self.lines.append( [ self.coord_index_of(l[0]),780self.coord_index_of(l[1]) ] )781if obj[j].is_ray():782l = [obj[i].vector(), obj[i].vector() + obj[j].vector()]783self.arrows.append( [ self.coord_index_of(l[0]),784self.coord_index_of(l[1]) ] )785if obj[j].is_line():786l1 = [obj[i].vector(), obj[i].vector() + obj[j].vector()]787l2 = [obj[i].vector(), obj[i].vector() - obj[j].vector()]788self.arrows.append( [ self.coord_index_of(l1[0]),789self.coord_index_of(l1[1]) ] )790self.arrows.append( [ self.coord_index_of(l2[0]),791self.coord_index_of(l2[1]) ] )792793794def _init_area_2d(self, polyhedron):795"""796Internal function: Initialize polygon area for 2d polyhedron.797798TESTS::799800sage: p = polytopes.cyclic_polytope(2,4)801sage: proj = p.projection()802sage: proj.polygons = Sequence([])803sage: proj._init_area_2d(p)804sage: proj.polygons805[[3, 0, 1, 2]]806"""807assert polyhedron.ambient_dim() == 2, "Requires polyhedron in 2d"808vertices = [v for v in polyhedron.Vrep_generator()]809vertices = cyclic_sort_vertices_2d(vertices)810coords = []811812def adjacent_vertices(i):813n = len(vertices)814if vertices[(i-1) % n].is_vertex(): yield vertices[(i-1) % n]815if vertices[(i+1) % n].is_vertex(): yield vertices[(i+1) % n]816817for i in range(len(vertices)):818v = vertices[i]819if v.is_vertex():820coords.append(v())821if v.is_ray():822for a in adjacent_vertices(i):823coords.append(a() + v())824825if polyhedron.n_lines() == 0:826self.polygons.append( self.coord_indices_of(coords) )827return828829polygons = []830831if polyhedron.n_lines() == 1:832aline = polyhedron.line_generator().next()833for shift in [aline(), -aline()]:834for i in range(len(coords)):835polygons.append( [ coords[i-1],coords[i],836coords[i]+shift, coords[i-1]+shift ] )837838if polyhedron.n_lines() == 2:839[line1, line2] = [l for l in polyhedron.lines()]840assert len(coords)==1, "Can have only a single vertex!"841v = coords[0]842l1 = line1()843l2 = line2()844polygons = [ [v-l1-l2, v+l1-l2, v+l1+l2, v-l1+l2] ]845846polygons = [ self.coord_indices_of(p) for p in polygons ]847self.polygons.extend(polygons)848849850851def _init_solid_3d(self, polyhedron):852"""853Internal function: Initialize facet polygons for 3d polyhedron.854855TESTS::856857sage: p = polytopes.cyclic_polytope(3,4)858sage: proj = p.projection()859sage: proj.polygons = Sequence([])860sage: proj._init_solid_3d(p)861sage: proj.polygons862[[2, 0, 1], [3, 0, 1], [3, 0, 2], [3, 1, 2]]863"""864assert polyhedron.ambient_dim() == 3, "Requires polyhedron in 3d"865866if polyhedron.dim() <= 1: # empty or 0d or 1d polyhedron => no polygon867return None868869def defining_equation(): # corresponding to a polygon870if polyhedron.dim() < 3:871yield polyhedron.equation_generator().next()872else:873for ineq in polyhedron.inequality_generator():874yield ineq875876faces = []877face_inequalities = []878for facet_equation in defining_equation():879vertices = [v for v in facet_equation.incident()]880face_inequalities.append(facet_equation)881vertices = cyclic_sort_vertices_2d(vertices)882coords = []883884def adjacent_vertices(i):885n = len(vertices)886if vertices[(i-1) % n].is_vertex(): yield vertices[(i-1) % n]887if vertices[(i+1) % n].is_vertex(): yield vertices[(i+1) % n]888889for i in range(len(vertices)):890v = vertices[i]891if v.is_vertex():892coords.append(v())893if v.is_ray():894for a in adjacent_vertices(i):895coords.append(a() + v())896897faces.append(coords)898self.face_inequalities = face_inequalities899900if polyhedron.n_lines() == 0:901assert len(faces)>0, "no vertices?"902self.polygons.extend( [self.coord_indices_of(f) for f in faces] )903return904905# now some special cases if there are lines (dim < ambient_dim)906polygons = []907908if polyhedron.n_lines()==1:909assert len(faces)>0, "no vertices?"910aline = polyhedron.line_generator().next()911for shift in [aline(), -aline()]:912for coords in faces:913assert len(coords)==2, "There must be two points."914polygons.append( [ coords[0],coords[1],915coords[1]+shift, coords[0]+shift ] )916917if polyhedron.n_lines()==2:918[line1, line2] = [l for l in polyhedron.line_generator()]919l1 = line1()920l2 = line2()921for v in polyhedron.vertex_generator():922polygons.append( [v()-l1-l2, v()+l1-l2, v()+l1+l2, v()-l1+l2] )923924self.polygons.extend( [self.coord_indices_of(p) for p in polygons] )925926927def render_points_2d(self, **kwds):928"""929Return the points of a polyhedron in 2d.930931EXAMPLES::932933sage: hex = polytopes.regular_polygon(6)934sage: proj = hex.projection()935sage: hex_points = proj.render_points_2d()936sage: hex_points._objects937[Point set defined by 6 point(s)]938"""939return point2d(self.coordinates_of(self.points), **kwds)940941942def render_outline_2d(self, **kwds):943"""944Return the outline (edges) of a polyhedron in 2d.945946EXAMPLES::947948sage: penta = polytopes.regular_polygon(5)949sage: outline = penta.projection().render_outline_2d()950sage: outline._objects[0]951Line defined by 2 points952"""953wireframe = [];954for l in self.lines:955l_coords = self.coordinates_of(l)956wireframe.append( line2d(l_coords, **kwds) )957for a in self.arrows:958a_coords = self.coordinates_of(a)959wireframe.append( arrow(a_coords[0], a_coords[1], **kwds) )960return sum(wireframe)961962963def render_fill_2d(self, **kwds):964"""965Return the filled interior (a polygon) of a polyhedron in 2d.966967EXAMPLES::968969sage: cps = [i^3 for i in srange(-2,2,1/5)]970sage: p = Polyhedron(vertices = [[(t^2-1)/(t^2+1),2*t/(t^2+1)] for t in cps])971sage: proj = p.projection()972sage: filled_poly = proj.render_fill_2d()973sage: filled_poly.axes_width()9740.8975"""976poly = [polygon2d(self.coordinates_of(p), **kwds)977for p in self.polygons]978return sum(poly)979980981def render_vertices_3d(self, **kwds):982"""983Return the 3d rendering of the vertices.984985EXAMPLES::986987sage: p = polytopes.cross_polytope(3)988sage: proj = p.projection()989sage: verts = proj.render_vertices_3d()990sage: verts.bounding_box()991((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0))992"""993return point3d(self.coordinates_of(self.points), **kwds)994995996def render_wireframe_3d(self, **kwds):997r"""998Return the 3d wireframe rendering.9991000EXAMPLES::10011002sage: cube = polytopes.n_cube(3)1003sage: cube_proj = cube.projection()1004sage: wire = cube_proj.render_wireframe_3d()1005sage: print wire.tachyon().split('\n')[77] # for testing1006FCylinder base -1.0 1.0 -1.0 apex -1.0 -1.0 -1.0 rad 0.005 texture...1007"""1008wireframe = [];1009for l in self.lines:1010l_coords = self.coordinates_of(l)1011wireframe.append( line3d(l_coords, **kwds))1012for a in self.arrows:1013a_coords = self.coordinates_of(a)1014wireframe.append(arrow3d(a_coords[0], a_coords[1], **kwds))1015return sum(wireframe)101610171018def render_solid_3d(self, **kwds):1019"""1020Return solid 3d rendering of a 3d polytope.10211022EXAMPLES::10231024sage: p = polytopes.n_cube(3).projection()1025sage: p_solid = p.render_solid_3d(opacity = .7)1026sage: type(p_solid)1027<class 'sage.plot.plot3d.base.Graphics3dGroup'>1028"""1029return sum([ polygon3d(self.coordinates_of(f), **kwds)1030for f in self.polygons ])10311032def tikz(self, view=[0,0,1], angle=0, scale=2,1033edge_color='blue!95!black', facet_color='blue!95!black',1034opacity=0.8, vertex_color='green', axis=False):1035r"""1036Return a string ``tikz_pic`` consisting of a tikz picture of ``self``1037according to a projection ``view`` and an angle ``angle``1038obtained via Jmol through the current state property.10391040INPUT:10411042- ``view`` - list (default: [0,0,1]) representing the rotation axis (see note below).1043- ``angle`` - integer (default: 0) angle of rotation in degree from 0 to 360 (see note1044below).1045- ``scale`` - integer (default: 2) specifying the scaling of the tikz picture.1046- ``edge_color`` - string (default: 'blue!95!black') representing colors which tikz1047recognize.1048- ``facet_color`` - string (default: 'blue!95!black') representing colors which tikz1049recognize.1050- ``vertex_color`` - string (default: 'green') representing colors which tikz1051recognize.1052- ``opacity`` - real number (default: 0.8) between 0 and 1 giving the opacity of1053the front facets.1054- ``axis`` - Boolean (default: False) draw the axes at the origin or not.10551056OUTPUT:10571058- LatexExpr -- containing the TikZ picture.10591060.. NOTE::10611062The inputs ``view`` and ``angle`` can be obtained from the1063viewer Jmol::106410651) Right click on the image10662) Select ``Console``10673) Select the tab ``State``10684) Scroll to the line ``moveto``10691070It reads something like::10711072moveto 0.0 {x y z angle} Scale10731074The ``view`` is then [x,y,z] and ``angle`` is angle.1075The following number is the scale.10761077Jmol performs a rotation of ``angle`` degrees along the1078vector [x,y,z] and show the result from the z-axis.10791080EXAMPLES::10811082sage: P1 = polytopes.small_rhombicuboctahedron()1083sage: Image1 = P1.projection().tikz([1,3,5], 175, scale=4)1084sage: type(Image1)1085<class 'sage.misc.latex.LatexExpr'>1086sage: print '\n'.join(Image1.splitlines()[:4])1087\begin{tikzpicture}%1088[x={(-0.939161cm, 0.244762cm)},1089y={(0.097442cm, -0.482887cm)},1090z={(0.329367cm, 0.840780cm)},1091sage: open('polytope-tikz1.tex', 'w').write(Image1) # not tested10921093sage: P2 = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]])1094sage: Image2 = P2.projection().tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True)1095sage: type(Image2)1096<class 'sage.misc.latex.LatexExpr'>1097sage: print '\n'.join(Image2.splitlines()[:4])1098\begin{tikzpicture}%1099[scale=3.000000,1100back/.style={loosely dotted, thin},1101edge/.style={color=blue!95!black, thick},1102sage: open('polytope-tikz2.tex', 'w').write(Image2) # not tested11031104sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]])1105sage: P31106A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices1107sage: Image3 = P3.projection().tikz([0.5,-1,-0.1], 55, scale=3, edge_color='blue!95!black',facet_color='orange!95!black', opacity=0.7, vertex_color='yellow', axis=True)1108sage: print '\n'.join(Image3.splitlines()[:4])1109\begin{tikzpicture}%1110[x={(0.658184cm, -0.242192cm)},1111y={(-0.096240cm, 0.912008cm)},1112z={(-0.746680cm, -0.331036cm)},1113sage: open('polytope-tikz3.tex', 'w').write(Image3) # not tested11141115sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]])1116sage: P1117A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices1118sage: P.projection().tikz()1119Traceback (most recent call last):1120...1121NotImplementedError: The polytope has to live in 2 or 3 dimensions.11221123.. TODO::11241125Make it possible to draw Schlegel diagram for 4-polytopes. ::11261127sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]])1128sage: P1129A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices1130sage: P.projection().tikz()1131Traceback (most recent call last):1132...1133NotImplementedError: The polytope has to live in 2 or 3 dimensions.11341135Make it possible to draw 3-polytopes living in higher dimension.1136"""1137if self.polyhedron_ambient_dim > 3 or self.polyhedron_ambient_dim < 2:1138raise NotImplementedError("The polytope has to live in 2 or 3 dimensions.")1139elif self.polyhedron_ambient_dim == 2:1140return self._tikz_2d(scale, edge_color, facet_color, opacity,1141vertex_color, axis)1142elif self.dimension == 2:1143return self._tikz_2d_in_3d(view, angle, scale, edge_color,1144facet_color, opacity, vertex_color, axis)1145else:1146return self._tikz_3d_in_3d(view, angle, scale, edge_color,1147facet_color, opacity, vertex_color, axis)11481149def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis):1150r"""1151Return a string ``tikz_pic`` consisting of a tikz picture of ``self``11521153INPUT:11541155- ``scale`` - integer specifying the scaling of the tikz picture.1156- ``edge_color`` - string representing colors which tikz1157recognize.1158- ``facet_color`` - string representing colors which tikz1159recognize.1160- ``vertex_color`` - string representing colors which tikz1161recognize.1162- ``opacity`` - real number between 0 and 1 giving the opacity of1163the front facets.1164- ``axis`` - Boolean (default: False) draw the axes at the origin or not.11651166OUTPUT:11671168- LatexExpr -- containing the TikZ picture.11691170EXAMPLES::11711172sage: P = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]])1173sage: Image = P.projection()._tikz_2d(scale=3, edge_color='black', facet_color='orange', opacity=0.75, vertex_color='yellow', axis=True)1174sage: type(Image)1175<class 'sage.misc.latex.LatexExpr'>1176sage: print '\n'.join(Image.splitlines()[:4])1177\begin{tikzpicture}%1178[scale=3.000000,1179back/.style={loosely dotted, thin},1180edge/.style={color=black, thick},1181sage: open('polytope-tikz2.tex', 'w').write(Image) # not tested11821183.. NOTE::11841185The ``facet_color`` is the filing color of the polytope (polygon).1186"""11871188# Creates the nodes, coordinate and tag for every vertex of the polytope.1189# The tag is used to draw the front facets later on.11901191dict_drawing = {}1192edges = ''1193for vert in self.points:1194v = self.coords[vert]1195v_vect = str([i.n(digits=3) for i in v])1196v_vect = v_vect.replace('[', '(')1197v_vect = v_vect.replace(']', ')')1198tag = '%s' %v_vect1199node = "\\node[%s] at %s {};\n" % ('vertex', tag)1200coord = '\coordinate %s at %s;\n' % (tag, tag)1201dict_drawing[vert] = node, coord, tag12021203for index1, index2 in self.lines:1204# v1 = self.coords[index1]1205# v2 = self.coords[index2]1206edges += "\\draw[%s] %s -- %s;\n" % ('edge',1207dict_drawing[index1][2],1208dict_drawing[index2][2])12091210# Start to write the output1211tikz_pic = ''1212tikz_pic += '\\begin{tikzpicture}%\n'1213tikz_pic += '\t[scale=%f,\n' % scale1214tikz_pic += '\tback/.style={loosely dotted, thin},\n'1215tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color1216tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity)1217tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color1218tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color12191220# Draws the axes if True1221if axis:1222tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'1223tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'12241225# Create the coordinate of the vertices:1226tikz_pic += '%% Coordinate of the vertices:\n%%\n'1227for v in dict_drawing:1228tikz_pic += dict_drawing[v][1]12291230# Draw the interior by going in a cycle1231vertices = list(self.parent_polyhedron.Vrep_generator())1232tikz_pic += '%%\n%%\n%% Drawing the interior\n%%\n'1233cyclic_vert = cyclic_sort_vertices_2d(list(self.parent_polyhedron.Vrep_generator()))1234cyclic_indices = [vertices.index(v) for v in cyclic_vert]1235tikz_pic += '\\fill[facet] '1236for v in cyclic_indices:1237if v in dict_drawing:1238tikz_pic += '%s -- ' % dict_drawing[v][2]1239tikz_pic += 'cycle {};\n'12401241# Draw the edges1242tikz_pic += '%%\n%%\n%% Drawing edges\n%%\n'1243tikz_pic += edges12441245# Finally, the vertices in front are drawn on top of everything.1246tikz_pic += '%%\n%%\n%% Drawing the vertices\n%%\n'1247for v in dict_drawing:1248tikz_pic += dict_drawing[v][0]1249tikz_pic += '%%\n%%\n\\end{tikzpicture}'12501251return LatexExpr(tikz_pic)12521253def _tikz_2d_in_3d(self, view, angle, scale, edge_color, facet_color,1254opacity, vertex_color, axis):1255r"""1256Return a string ``tikz_pic`` consisting of a tikz picture of ``self``1257according to a projection ``view`` and an angle ``angle``1258obtained via Jmol through the current state property.12591260INPUT:12611262- ``view`` - list (default: [0,0,1]) representing the rotation axis.1263- ``angle`` - integer angle of rotation in degree from 0 to 360.1264- ``scale`` - integer specifying the scaling of the tikz picture.1265- ``edge_color`` - string representing colors which tikz1266recognize.1267- ``facet_color`` - string representing colors which tikz1268recognize.1269- ``vertex_color`` - string representing colors which tikz1270recognize.1271- ``opacity`` - real number between 0 and 1 giving the opacity of1272the front facets.1273- ``axis`` - Boolean draw the axes at the origin or not.12741275OUTPUT:12761277- LatexExpr -- containing the TikZ picture.12781279EXAMPLE::12801281sage: P = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]])1282sage: P1283A 2-dimensional polyhedron in ZZ^3 defined as the convex hull of 3 vertices1284sage: Image = P.projection()._tikz_2d_in_3d(view=[0.5,-1,-0.5], angle=55, scale=3, edge_color='blue!95!black', facet_color='orange', opacity=0.5, vertex_color='yellow', axis=True)1285sage: print '\n'.join(Image.splitlines()[:4])1286\begin{tikzpicture}%1287[x={(0.644647cm, -0.476559cm)},1288y={(0.192276cm, 0.857859cm)},1289z={(-0.739905cm, -0.192276cm)},1290sage: open('polytope-tikz3.tex', 'w').write(Image) # not tested12911292.. NOTE::12931294The ``facet_color`` is the filing color of the polytope (polygon).1295"""1296view_vector = vector(RDF, view)1297rot = rotate_arbitrary(view_vector,-(angle/360)*2*pi)1298rotation_matrix = rot[:2].transpose()12991300# Creates the nodes, coordinate and tag for every vertex of the polytope.1301# The tag is used to draw the front facets later on.1302dict_drawing = {}1303edges = ''1304for vert in self.points:1305v = self.coords[vert]1306v_vect = str([i.n(digits=3) for i in v])1307v_vect = v_vect.replace('[','(')1308v_vect = v_vect.replace(']',')')1309tag = '%s' %v_vect1310node = "\\node[%s] at %s {};\n" % ('vertex', tag)1311coord = '\coordinate %s at %s;\n' % (tag, tag)1312dict_drawing[vert] = node, coord, tag13131314for index1, index2 in self.lines:1315# v1 = self.coords[index1]1316# v2 = self.coords[index2]1317edges += "\\draw[%s] %s -- %s;\n" % ('edge',1318dict_drawing[index1][2],1319dict_drawing[index2][2])13201321# Start to write the output1322tikz_pic = ''1323tikz_pic += '\\begin{tikzpicture}%\n'1324tikz_pic += '\t[x={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[0][0]),1325RDF(rotation_matrix[0][1]))1326tikz_pic += '\ty={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[1][0]),1327RDF(rotation_matrix[1][1]))1328tikz_pic += '\tz={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[2][0]),1329RDF(rotation_matrix[2][1]))1330tikz_pic += '\tscale=%f,\n' % scale1331tikz_pic += '\tback/.style={loosely dotted, thin},\n'1332tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color1333tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity)1334tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color1335tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color13361337# Draws the axes if True1338if axis:1339tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'1340tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'1341tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'13421343# Create the coordinate of the vertices:1344tikz_pic += '%% Coordinate of the vertices:\n%%\n'1345for v in dict_drawing:1346tikz_pic += dict_drawing[v][1]13471348# Draw the interior by going in a cycle1349vertices = list(self.parent_polyhedron.Vrep_generator())1350tikz_pic += '%%\n%%\n%% Drawing the interior\n%%\n'1351cyclic_vert = cyclic_sort_vertices_2d(list(self.parent_polyhedron.Vrep_generator()))1352cyclic_indices = [vertices.index(v) for v in cyclic_vert]1353tikz_pic += '\\fill[facet] '1354for v in cyclic_indices:1355if v in dict_drawing:1356tikz_pic += '%s -- ' % dict_drawing[v][2]1357tikz_pic += 'cycle {};\n'13581359# Draw the edges in the front1360tikz_pic += '%%\n%%\n%% Drawing edges\n%%\n'1361tikz_pic += edges13621363# Finally, the vertices in front are drawn on top of everything.1364tikz_pic += '%%\n%%\n%% Drawing the vertices\n%%\n'1365for v in dict_drawing:1366tikz_pic += dict_drawing[v][0]1367tikz_pic += '%%\n%%\n\\end{tikzpicture}'13681369return LatexExpr(tikz_pic)13701371def _tikz_3d_in_3d(self, view, angle, scale, edge_color,1372facet_color, opacity, vertex_color, axis):1373r"""1374Return a string ``tikz_pic`` consisting of a tikz picture of ``self``1375according to a projection ``view`` and an angle ``angle``1376obtained via Jmol through the current state property.13771378INPUT:13791380- ``view`` - list (default: [0,0,1]) representing the rotation axis.1381- ``angle`` - integer angle of rotation in degree from 0 to 360.1382- ``scale`` - integer specifying the scaling of the tikz picture.1383- ``edge_color`` - string representing colors which tikz1384recognize.1385- ``facet_color`` - string representing colors which tikz1386recognize.1387- ``vertex_color`` - string representing colors which tikz1388recognize.1389- ``opacity`` - real number between 0 and 1 giving the opacity of1390the front facets.1391- ``axis`` - Boolean draw the axes at the origin or not.13921393OUTPUT:13941395- LatexExpr -- containing the TikZ picture.13961397EXAMPLES::13981399sage: P = polytopes.small_rhombicuboctahedron()1400sage: Image = P.projection()._tikz_3d_in_3d([3,7,5], 100, scale=3, edge_color='blue', facet_color='orange', opacity=0.5, vertex_color='green', axis=True)1401sage: type(Image)1402<class 'sage.misc.latex.LatexExpr'>1403sage: print '\n'.join(Image.splitlines()[:4])1404\begin{tikzpicture}%1405[x={(-0.046385cm, 0.837431cm)},1406y={(-0.243536cm, 0.519228cm)},1407z={(0.968782cm, 0.170622cm)},1408sage: open('polytope-tikz1.tex', 'w').write(Image) # not tested1409"""1410view_vector = vector(RDF, view)1411rot = rotate_arbitrary(view_vector, -(angle/360)*2*pi)1412rotation_matrix = rot[:2].transpose()1413proj_vector = (rot**(-1))*vector(RDF, [0, 0, 1])14141415# First compute the back and front vertices and facets1416facets = self.face_inequalities1417front_facets = []1418back_facets = []1419for index_facet in range(len(facets)):1420A = facets[index_facet].vector()[1:]1421B = facets[index_facet].vector()[0]1422if A*(2000*proj_vector)+B < 0:1423front_facets += [index_facet]1424else:1425back_facets += [index_facet]14261427vertices = list(self.parent_polyhedron.Vrep_generator())1428front_vertices = []1429for index_facet in front_facets:1430A = facets[index_facet].vector()[1:]1431B = facets[index_facet].vector()[0]1432for v in self.points:1433if A*self.coords[v]+B < 0.0005 and v not in front_vertices:1434front_vertices += [v]14351436back_vertices = []1437for index_facet in back_facets:1438A = facets[index_facet].vector()[1:]1439B = facets[index_facet].vector()[0]1440for v in self.points:1441if A*self.coords[v]+B < 0.0005 and v not in back_vertices:1442back_vertices += [v]14431444# Creates the nodes, coordinate and tag for every vertex of the polytope.1445# The tag is used to draw the front facets later on.1446dict_drawing = {}1447back_part = ''1448front_part = ''14491450for vert in self.points:1451v = self.coords[vert]1452v_vect = str([i.n(digits=3) for i in v])1453v_vect = v_vect.replace('[','(')1454v_vect = v_vect.replace(']',')')1455tag = '%s' %v_vect1456node = "\\node[%s] at %s {};\n" % ('vertex', tag)1457coord = '\coordinate %s at %s;\n' %(tag, tag)1458dict_drawing[vert] = node, coord, tag14591460# Separate the edges between back and front14611462for index1, index2 in self.lines:1463# v1 = self.coords[index1]1464# v2 = self.coords[index2]14651466H_v1 = set(self.parent_polyhedron.Vrepresentation(index1).incident())1467H_v2 = set(self.parent_polyhedron.Vrepresentation(index2).incident())1468H_v12 = [h for h in H_v1.intersection(H_v2) if h in back_facets]14691470# The back edge has to be between two vertices in the Back1471# AND such that the 2 facets touching them are in the Back1472if index1 in back_vertices and index2 in back_vertices and len(H_v12)==2:1473back_part += "\\draw[%s,back] %s -- %s;\n" % ('edge', dict_drawing[index1][2], dict_drawing[index2][2])1474else:1475front_part += "\\draw[%s] %s -- %s;\n" % ('edge',dict_drawing[index1][2],dict_drawing[index2][2])14761477# Start to write the output1478tikz_pic = ''1479tikz_pic += '\\begin{tikzpicture}%\n'1480tikz_pic += '\t[x={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[0][0]),1481RDF(rotation_matrix[0][1]))1482tikz_pic += '\ty={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[1][0]),1483RDF(rotation_matrix[1][1]))1484tikz_pic += '\tz={(%fcm, %fcm)},\n' % (RDF(rotation_matrix[2][0]),1485RDF(rotation_matrix[2][1]))1486tikz_pic += '\tscale=%f,\n' % scale1487tikz_pic += '\tback/.style={loosely dotted, thin},\n'1488tikz_pic += '\tedge/.style={color=%s, thick},\n' % edge_color1489tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' % (facet_color,opacity)1490tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' % vertex_color1491tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n%%\n%%\n' % vertex_color14921493# Draws the axes if True1494if axis:1495tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'1496tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'1497tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'14981499# Create the coordinate of the vertices1500tikz_pic += '%% Coordinate of the vertices:\n%%\n'1501for v in dict_drawing:1502tikz_pic += dict_drawing[v][1]15031504# Draw the edges in the back1505tikz_pic += '%%\n%%\n%% Drawing edges in the back\n%%\n'1506tikz_pic += back_part15071508# Draw the vertices on top of the back-edges1509tikz_pic += '%%\n%%\n%% Drawing vertices in the back\n%%\n'1510for v in back_vertices:1511if not v in front_vertices and v in dict_drawing:1512tikz_pic += dict_drawing[v][0]15131514# Draw the facets in the front by going in cycles for every facet.1515tikz_pic += '%%\n%%\n%% Drawing the facets\n%%\n'1516for index_facet in front_facets:1517cyclic_vert = cyclic_sort_vertices_2d(list(facets[index_facet].incident()))1518cyclic_indices = [vertices.index(v) for v in cyclic_vert]1519tikz_pic += '\\fill[facet] '1520for v in cyclic_indices:1521if v in dict_drawing:1522tikz_pic += '%s -- ' % dict_drawing[v][2]1523tikz_pic += 'cycle {};\n'15241525# Draw the edges in the front1526tikz_pic += '%%\n%%\n%% Drawing edges in the front\n%%\n'1527tikz_pic += front_part15281529# Finally, the vertices in front are drawn on top of everything.1530tikz_pic += '%%\n%%\n%% Drawing the vertices in the front\n%%\n'1531for v in self.points:1532if v in front_vertices:1533if v in dict_drawing:1534tikz_pic += dict_drawing[v][0]1535tikz_pic += '%%\n%%\n\\end{tikzpicture}'1536return LatexExpr(tikz_pic)153715381539