# Ticket #12083: trac12083_tikz_polytope.2.patch

File trac12083_tikz_polytope.2.patch, 19.4 KB (added by jipilab, 11 years ago)

Addition of tikz method for polyhedron

• ## sage/geometry/polyhedra.py

# HG changeset patch
# User Jean-Philippe Labbé <labbe at math.fu-berlin.de>
# Date 1323018531 -3600
# Node ID b2afa5c1da2fb5cfd930d55f961f2de97682ed6b
# Parent  dd6b5b7c3bf813b9125669f6b16e8390bab2b7c4
trac #12083: Added the tikz method to polyhedron

diff --git a/sage/geometry/polyhedra.py b/sage/geometry/polyhedra.py
 a - Sebastien Barthelemy: documentation improvements, 2008 - Volker Braun: refactoring, handle non-compact case, 2009 and 2010 - Andrey Novoseltsev: added Hasse_diagram_from_incidences, 2010 - Jean-Philippe Labb\'e: added the tikz method for 2,3-polytope, 2011 TESTS:: from subprocess import Popen, PIPE from sage.misc.all import tmp_filename from sage.misc.functional import norm from sage.misc.functional import norm, n from sage.misc.package import is_package_installed from sage.rings.all import Integer, QQ, ZZ, primes_first_n from sage.rings.real_double import RDF from sage.modules.free_module_element import vector from sage.modules.free_module import VectorSpace from sage.matrix.constructor import matrix, identity_matrix from sage.functions.other import sqrt, floor, ceil from sage.functions.trig import sin, cos from sage.symbolic.constants import pi from sage.plot.all import point2d, line2d, arrow, polygon2d from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d from sage.plot.plot3d.transform import rotate_arbitrary from sage.graphs.graph import Graph from sage.combinat.combinat import permutations self._restricted_automorphism_group = group return group def tikz(self, view=[0,0,1], rot_angle=0, scale=2, edge_color='blue!95!black', facet_color='blue!95!black', opacity=0.8, vertex_color='green', axis=False): """ Return a string tikz_pic consisting of a tikz picture of self according to a projection view and an angle rot_angle obtained via Jmol through the current state property. INPUT: - view -- a list of length 3 representing a vector; - rot_angle -- an angle in degree from 0 to 360; - scale -- an integer specifying the scaling of the tikz picture; - edge_color, facet_color, vertex_color -- string representing colors which tikz recognize; - opacity -- a real number between 0 and 1 giving the opacity of the front facets; - axis -- a Boolean, to draw the axes at the origin. OUTPUT: - tikz_pic -- a string containing the raw text of a TikZ picture. .. NOTE:: The inputs view and rot_angle can be obtained from the viewer Jmol: 1) Right click on the image; 2) Select Console; 3) Select the tab State; 4) Scroll to the line moveto It read something like: moveto 0.0 {x y z angle} Scale The view is then [x,y,z] and rot_angle is angle. The following number is the scale. EXAMPLES:: sage: P1 = polytopes.small_rhombicuboctahedron() sage: Image1 = P1.tikz([1,3,5], 175, scale=4) sage: open('polytope-tikz.tex', 'w').write(Image1) sage: P2 = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]]) sage: Image2 = P2.tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True) sage: open('polytope-tikz2.tex', 'w').write(Image2) sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]]) sage: P3 A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices. sage: Image3 = P3.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) sage: open('polytope-tikz3.tex', 'w').write(Image3) sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) sage: P A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices. sage: P.tikz() Traceback (most recent call last): ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. .. TO DO:: Make it possible to draw Schlegel diagram for 4-polytope. sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]]) sage: P A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices. sage: P.tikz() Traceback (most recent call last): ... NotImplementedError: The polytope has to live in 2 or 3 dimensions. Make it possible to draw 3-polytope living in higher dimension. """ if self.ambient_dim() > 3 or self.ambient_dim() < 2: raise NotImplementedError("The polytope has to live in 2 or 3 dimensions.") elif self.ambient_dim() == 2: return self._tikz_2d(scale, edge_color, facet_color, opacity, vertex_color, axis) elif self.dim() == 2: return self._tikz_2d_in_3d(view, rot_angle, scale, edge_color, facet_color, opacity, vertex_color, axis) else: return self._tikz_3d_in_3d(view, rot_angle, scale, edge_color, facet_color, opacity, vertex_color, axis) def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis): """ Return a string tikz_pic consisting of a tikz picture of self INPUT: - scale -- an integer specifying the scaling of the tikz picture; - edge_color, facet_color, vertex_color -- string representing colors which tikz recognize; - opacity -- a real number between 0 and 1 giving the opacity of the front facets; - axis -- a Boolean, to draw the axes at the origin. .. NOTE:: The facet_color is the filing color of the polytope (polygon). """ # Creates the nodes, coordinate and tag for every vertex of the polytope. # The tag is used to draw the front facets later on. dict_drawing = {} edges = '' for v in self.Vrep_generator(): v_vect = str([i.n(digits=3) for i in v.vector()]) v_vect = v_vect.replace('[','(') v_vect = v_vect.replace(']',')') tag = '%s' %v_vect node = "\\node[%s] at %s     {};\n" %('vertex',tag) for nei in v.neighbors(): nei_vect = str([i.n(digits=3) for i in nei.vector()]) nei_vect = nei_vect.replace('[','(') nei_vect = nei_vect.replace(']',')') tag_nei = '%s' %nei_vect edges += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei) coord = '\coordinate %s at %s;\n' %(tag,tag) dict_drawing[v] = node,coord,tag # Start to write the output tikz_pic = '' tikz_pic += '\\begin{tikzpicture}%\n' tikz_pic += '\t[scale=%f,\n' %scale tikz_pic += '\tback/.style={loosely dotted, thin},\n' tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color # Draws the axes if True if axis: tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' # Create the coordinate of the vertices: tikz_pic += '%% Coordinate of the vertices:\n\n' for v in self.Vrep_generator(): tikz_pic += dict_drawing[v][1] # Draw the interior by going in a cycle tikz_pic += '\n\n%% Drawing the interior\n\n' cyclic_vert = cyclic_sort_vertices_2d(list(self.Vrep_generator())) tikz_pic += '\\fill[facet] ' for v in cyclic_vert: tikz_pic += '%s -- ' %dict_drawing[v][2] tikz_pic += 'cycle {};\n' # Draw the edges in the front tikz_pic += '\n\n%% Drawing edges in the front\n\n' tikz_pic += edges # Finally, the vertices in front are drawn on top of everything. tikz_pic += '\n\n%% Drawing the vertices in the front\n\n' for v in self.Vrep_generator(): tikz_pic += dict_drawing[v][0] tikz_pic += '\n\n\\end{tikzpicture}' return tikz_pic def _tikz_2d_in_3d(self, view, rot_angle, scale, edge_color, facet_color, opacity, vertex_color, axis): """ Return a string tikz_pic consisting of a tikz picture of self according to a projection view and an angle rot_angle obtained via Jmol through the current state property. INPUT: - view -- a list of length 3 representing a vector; - rot_angle -- an angle in degree from 0 to 360 (rotation along the view); - scale -- an integer specifying the scaling of the tikz picture; - edge_color, facet_color, vertex_color -- string representing colors which tikz recognize; - opacity -- a real number between 0 and 1 giving the opacity of the front facets; - axis -- a Boolean, to draw the axes at the origin. .. NOTE:: The facet_color is the filing color of the polytope (polygon). """ V = VectorSpace(RDF,3) view_vector = V(view) rot = rotate_arbitrary(view_vector,-(rot_angle/360)*2*pi) rotation_matrix = rot[:2].transpose() # Creates the nodes, coordinate and tag for every vertex of the polytope. # The tag is used to draw the front facets later on. dict_drawing = {} edges = '' for v in self.Vrep_generator(): v_vect = str([i.n(digits=3) for i in v.vector()]) v_vect = v_vect.replace('[','(') v_vect = v_vect.replace(']',')') tag = '%s' %v_vect node = "\\node[%s] at %s     {};\n" %('vertex',tag) for nei in v.neighbors(): nei_vect = str([i.n(digits=3) for i in nei.vector()]) nei_vect = nei_vect.replace('[','(') nei_vect = nei_vect.replace(']',')') tag_nei = '%s' %nei_vect edges += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei) coord = '\coordinate %s at %s;\n' %(tag,tag) dict_drawing[v] = node,coord,tag # Start to write the output tikz_pic = '' tikz_pic += '\\begin{tikzpicture}%\n' tikz_pic += '\t[x={(%fcm, %fcm)},\n' %(n(rotation_matrix[0][0]),n(rotation_matrix[0][1])) tikz_pic += '\ty={(%fcm, %fcm)},\n' %(n(rotation_matrix[1][0]),n(rotation_matrix[1][1])) tikz_pic += '\tz={(%fcm, %fcm)},\n' %(n(rotation_matrix[2][0]),n(rotation_matrix[2][1])) tikz_pic += '\tscale=%f,\n' %scale tikz_pic += '\tback/.style={loosely dotted, thin},\n' tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color # Draws the axes if True if axis: tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n' # Create the coordinate of the vertices: tikz_pic += '%% Coordinate of the vertices:\n\n' for v in self.Vrep_generator(): tikz_pic += dict_drawing[v][1] # Draw the interior by going in a cycle tikz_pic += '\n\n%% Drawing the interior\n\n' cyclic_vert = cyclic_sort_vertices_2d(list(self.Vrep_generator())) tikz_pic += '\\fill[facet] ' for v in cyclic_vert: tikz_pic += '%s -- ' %dict_drawing[v][2] tikz_pic += 'cycle {};\n' # Draw the edges in the front tikz_pic += '\n\n%% Drawing edges in the front\n\n' tikz_pic += edges # Finally, the vertices in front are drawn on top of everything. tikz_pic += '\n\n%% Drawing the vertices in the front\n\n' for v in self.Vrep_generator(): tikz_pic += dict_drawing[v][0] tikz_pic += '\n\n\\end{tikzpicture}' return tikz_pic def _tikz_3d_in_3d(self, view, rot_angle, scale, edge_color, facet_color, opacity, vertex_color, axis): """ Return a string tikz_pic consisting of a tikz picture of self according to a projection view and an angle rot_angle obtained via Jmol through the current state property. INPUT: - view -- a list of length 3 representing a vector; - rot_angle -- an angle in degree from 0 to 360; - scale -- an integer specifying the scaling of the tikz picture; - edge_color, facet_color, vertex_color -- string representing colors which tikz recognize; - opacity -- a real number between 0 and 1 giving the opacity of the front facets; - axis -- a Boolean, to draw the axes at the origin. """ V = VectorSpace(RDF,3) view_vector = V(view) rot = rotate_arbitrary(view_vector,-(rot_angle/360)*2*pi) rotation_matrix = rot[:2].transpose() proj_vector = (rot**(-1))*V([0,0,1]) # First compute the back and front vertices and facets front_facets = [] back_facets = [] for facet in self.Hrep_generator(): A = facet.vector()[1:] B = facet.vector()[0] if A*(2000*proj_vector)+B<0: front_facets += [facet] else: back_facets += [facet] front_vertices = [] for facet in front_facets: A = facet.vector()[1:] B = facet.vector()[0] for v in self.Vrep_generator(): if A*v.vector()+B<0.0005: front_vertices += [v] back_vertices = [] for facet in back_facets: A = facet.vector()[1:] B = facet.vector()[0] for v in self.Vrep_generator(): if A*v.vector()+B<0.0005: back_vertices += [v] # Creates the nodes, coordinate and tag for every vertex of the polytope. # The tag is used to draw the front facets later on. dict_drawing = {} first_part = '' second_part = '' for v in self.Vrep_generator(): v_vect = str([i.n(digits=3) for i in v.vector()]) v_vect = v_vect.replace('[','(') v_vect = v_vect.replace(']',')') tag = '%s' %v_vect node = "\\node[%s] at %s     {};\n" %('vertex',tag) for nei in v.neighbors(): nei_vect = str([i.n(digits=3) for i in nei.vector()]) nei_vect = nei_vect.replace('[','(') nei_vect = nei_vect.replace(']',')') tag_nei = '%s' %nei_vect H_v = set(v.incident()) H_nei = set(nei.incident()) H_v_nei = [h in back_facets for h in H_v.intersection(H_nei)] # The back edge has to be between two vertices in the Back # AND such that the 2 facets touching them are in the Back if v in back_vertices and nei in back_vertices and sum(H_v_nei)==2: first_part += "\\draw[%s,back] %s -- %s;\n" %('edge',tag,tag_nei) else: second_part += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei) coord = '\coordinate %s at %s;\n' %(tag,tag) dict_drawing[v] = node,coord,tag # Start to write the output tikz_pic = '' tikz_pic += '\\begin{tikzpicture}%\n' tikz_pic += '\t[x={(%fcm, %fcm)},\n' %(n(rotation_matrix[0][0]),n(rotation_matrix[0][1])) tikz_pic += '\ty={(%fcm, %fcm)},\n' %(n(rotation_matrix[1][0]),n(rotation_matrix[1][1])) tikz_pic += '\tz={(%fcm, %fcm)},\n' %(n(rotation_matrix[2][0]),n(rotation_matrix[2][1])) tikz_pic += '\tscale=%f,\n' %scale tikz_pic += '\tback/.style={loosely dotted, thin},\n' tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity) tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color # Draws the axes if True if axis: tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n' tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n' # Create the coordinate of the vertices tikz_pic += '%% Coordinate of the vertices:\n\n' for v in self.Vrep_generator(): tikz_pic += dict_drawing[v][1] # Draw the edges in the back tikz_pic += '\n\n%% Drawing edges in the back\n\n' tikz_pic += first_part # Draw the vertices on top of the back-edges tikz_pic += '\n\n%% Drawing vertices in the back\n\n' for v in self.Vrep_generator(): if v in back_vertices and v not in front_vertices: tikz_pic += dict_drawing[v][0] # Draw the facets in the front by going in cycles for every facet. tikz_pic += '\n\n%% Drawing the facets\n\n' for facet in front_facets: cyclic_vert = cyclic_sort_vertices_2d(list(facet.incident())) tikz_pic += '\\fill[facet] ' for v in cyclic_vert: tikz_pic += '%s -- ' %dict_drawing[v][2] tikz_pic += 'cycle {};\n' # Draw the edges in the front tikz_pic += '\n\n%% Drawing edges in the front\n\n' tikz_pic += second_part # Finally, the vertices in front are drawn on top of everything. tikz_pic += '\n\n%% Drawing the vertices in the front\n\n' for v in self.Vrep_generator(): if v in front_vertices: tikz_pic += dict_drawing[v][0] tikz_pic += '\n\n\\end{tikzpicture}' return tikz_pic ############################################################# def cyclic_sort_vertices_2d(Vlist):