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 b  
    9696    - Sebastien Barthelemy: documentation improvements, 2008
    9797    - Volker Braun: refactoring, handle non-compact case, 2009 and 2010
    9898    - Andrey Novoseltsev: added Hasse_diagram_from_incidences, 2010
     99    - Jean-Philippe Labb\'e: added the tikz method for 2,3-polytope, 2011
    99100
    100101TESTS::
    101102
     
    119120
    120121from subprocess import Popen, PIPE
    121122from sage.misc.all import tmp_filename
    122 from sage.misc.functional import norm
     123from sage.misc.functional import norm, n
    123124from sage.misc.package import is_package_installed
    124125
    125126from sage.rings.all import Integer, QQ, ZZ, primes_first_n
    126127from sage.rings.real_double import RDF
    127128from sage.modules.free_module_element import vector
     129from sage.modules.free_module import VectorSpace
    128130from sage.matrix.constructor import matrix, identity_matrix
    129131from sage.functions.other import sqrt, floor, ceil
    130132from sage.functions.trig import sin, cos
     133from sage.symbolic.constants import pi
    131134
    132135from sage.plot.all import point2d, line2d, arrow, polygon2d
    133136from sage.plot.plot3d.all import point3d, line3d, arrow3d, polygon3d
     137from sage.plot.plot3d.transform import rotate_arbitrary
    134138from sage.graphs.graph import Graph
    135139
    136140from sage.combinat.combinat import permutations
     
    48754879        self._restricted_automorphism_group = group
    48764880        return group
    48774881
     4882    def tikz(self, view=[0,0,1], rot_angle=0, scale=2,
     4883             edge_color='blue!95!black', facet_color='blue!95!black',
     4884             opacity=0.8, vertex_color='green', axis=False):
     4885        """
     4886        Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
     4887        according to a projection ``view`` and an angle ``rot_angle``
     4888        obtained via Jmol through the current state property.
     4889
     4890        INPUT:
     4891
     4892        - ``view`` -- a list of length 3 representing a vector;
     4893        - ``rot_angle`` -- an angle in degree from 0 to 360;
     4894        - ``scale`` -- an integer specifying the scaling of the tikz picture;
     4895        - ``edge_color``, ``facet_color``, ``vertex_color`` -- string representing
     4896          colors which tikz recognize;
     4897        - ``opacity`` -- a real number between 0 and 1 giving the opacity of
     4898          the front facets;
     4899        - ``axis`` -- a Boolean, to draw the axes at the origin.
     4900
     4901        OUTPUT:
     4902
     4903        - ``tikz_pic`` -- a string containing the raw text of a TikZ picture.
     4904
     4905        .. NOTE::
     4906
     4907            The inputs ``view`` and ``rot_angle`` can be obtained from the viewer
     4908            Jmol:
     4909
     4910            1) Right click on the image;
     4911            2) Select ``Console`;
     4912            3) Select the tab ``State`;
     4913            4) Scroll to the line ``moveto``
     4914
     4915            It read something like:
     4916
     4917            moveto 0.0 {x y z angle} Scale
     4918
     4919            The ``view`` is then [x,y,z] and ``rot_angle`` is angle.
     4920            The following number is the scale.
     4921
     4922        EXAMPLES::
     4923
     4924            sage: P1 = polytopes.small_rhombicuboctahedron()
     4925            sage: Image1 = P1.tikz([1,3,5], 175, scale=4)
     4926            sage: open('polytope-tikz.tex', 'w').write(Image1)
     4927
     4928            sage: P2 = Polyhedron(vertices=[[1, 1],[1, 2],[2, 1]])
     4929            sage: Image2 = P2.tikz(scale=3, edge_color='blue!95!black', facet_color='orange!95!black', opacity=0.4, vertex_color='yellow', axis=True)
     4930            sage: open('polytope-tikz2.tex', 'w').write(Image2)
     4931
     4932            sage: P3 = Polyhedron(vertices=[[-1, -1, 2],[-1, 2, -1],[2, -1, -1]])
     4933            sage: P3
     4934            A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices.
     4935            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)
     4936            sage: open('polytope-tikz3.tex', 'w').write(Image3)
     4937
     4938            sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]])
     4939            sage: P
     4940            A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices.
     4941            sage: P.tikz()
     4942            Traceback (most recent call last):
     4943            ...
     4944            NotImplementedError: The polytope has to live in 2 or 3 dimensions.
     4945
     4946
     4947        .. TO DO::
     4948
     4949            Make it possible to draw Schlegel diagram for 4-polytope.
     4950
     4951                sage: P=Polyhedron(vertices=[[1,1,0,0],[1,2,0,0],[2,1,0,0],[0,0,1,0],[0,0,0,1]])
     4952                sage: P
     4953                A 4-dimensional polyhedron in QQ^4 defined as the convex hull of 5 vertices.
     4954                sage: P.tikz()
     4955                Traceback (most recent call last):
     4956                ...
     4957                NotImplementedError: The polytope has to live in 2 or 3 dimensions.
     4958
     4959            Make it possible to draw 3-polytope living in higher dimension.
     4960
     4961        """
     4962        if self.ambient_dim() > 3 or self.ambient_dim() < 2:
     4963            raise NotImplementedError("The polytope has to live in 2 or 3 dimensions.")
     4964        elif self.ambient_dim() == 2:
     4965            return self._tikz_2d(scale, edge_color, facet_color, opacity,
     4966                                 vertex_color, axis)
     4967        elif self.dim() == 2:
     4968            return self._tikz_2d_in_3d(view, rot_angle, scale, edge_color,
     4969                                       facet_color, opacity, vertex_color, axis)
     4970        else:
     4971            return self._tikz_3d_in_3d(view, rot_angle, scale, edge_color,
     4972                                       facet_color, opacity, vertex_color, axis)
     4973
     4974    def _tikz_2d(self, scale, edge_color, facet_color, opacity, vertex_color, axis):
     4975        """
     4976        Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
     4977
     4978        INPUT:
     4979
     4980        - ``scale`` -- an integer specifying the scaling of the tikz picture;
     4981        - ``edge_color``, ``facet_color``, ``vertex_color`` -- string representing
     4982          colors which tikz recognize;
     4983        - ``opacity`` -- a real number between 0 and 1 giving the opacity of
     4984          the front facets;
     4985        - ``axis`` -- a Boolean, to draw the axes at the origin.
     4986
     4987        .. NOTE::
     4988
     4989            The ``facet_color`` is the filing color of the polytope (polygon).
     4990        """
     4991
     4992        # Creates the nodes, coordinate and tag for every vertex of the polytope.
     4993        # The tag is used to draw the front facets later on.
     4994        dict_drawing = {}
     4995        edges = ''
     4996        for v in self.Vrep_generator():
     4997            v_vect = str([i.n(digits=3) for i in v.vector()])
     4998            v_vect = v_vect.replace('[','(')
     4999            v_vect = v_vect.replace(']',')')
     5000            tag = '%s' %v_vect
     5001            node = "\\node[%s] at %s     {};\n" %('vertex',tag)
     5002
     5003            for nei in v.neighbors():
     5004                nei_vect = str([i.n(digits=3) for i in nei.vector()])
     5005                nei_vect = nei_vect.replace('[','(')
     5006                nei_vect = nei_vect.replace(']',')')
     5007                tag_nei = '%s' %nei_vect
     5008                edges += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei)
     5009
     5010            coord = '\coordinate %s at %s;\n' %(tag,tag)
     5011            dict_drawing[v] = node,coord,tag
     5012
     5013        # Start to write the output
     5014        tikz_pic = ''
     5015        tikz_pic += '\\begin{tikzpicture}%\n'
     5016        tikz_pic += '\t[scale=%f,\n' %scale
     5017        tikz_pic += '\tback/.style={loosely dotted, thin},\n'
     5018        tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color
     5019        tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity)
     5020        tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color
     5021        tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color
     5022
     5023        # Draws the axes if True
     5024        if axis:
     5025            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
     5026            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'
     5027
     5028        # Create the coordinate of the vertices:
     5029        tikz_pic += '%% Coordinate of the vertices:\n\n'
     5030        for v in self.Vrep_generator():
     5031            tikz_pic += dict_drawing[v][1]
     5032
     5033        # Draw the interior by going in a cycle
     5034        tikz_pic += '\n\n%% Drawing the interior\n\n'
     5035        cyclic_vert = cyclic_sort_vertices_2d(list(self.Vrep_generator()))
     5036        tikz_pic += '\\fill[facet] '
     5037        for v in cyclic_vert:
     5038            tikz_pic += '%s -- ' %dict_drawing[v][2]
     5039        tikz_pic += 'cycle {};\n'
     5040
     5041        # Draw the edges in the front
     5042        tikz_pic += '\n\n%% Drawing edges in the front\n\n'
     5043        tikz_pic += edges
     5044
     5045        # Finally, the vertices in front are drawn on top of everything.
     5046        tikz_pic += '\n\n%% Drawing the vertices in the front\n\n'
     5047        for v in self.Vrep_generator():
     5048            tikz_pic += dict_drawing[v][0]
     5049        tikz_pic += '\n\n\\end{tikzpicture}'
     5050
     5051        return tikz_pic
     5052
     5053    def _tikz_2d_in_3d(self, view, rot_angle, scale, edge_color, facet_color,
     5054                       opacity, vertex_color, axis):
     5055        """
     5056        Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
     5057        according to a projection ``view`` and an angle ``rot_angle``
     5058        obtained via Jmol through the current state property.
     5059
     5060        INPUT:
     5061
     5062        - ``view`` -- a list of length 3 representing a vector;
     5063        - ``rot_angle`` -- an angle in degree from 0 to 360 (rotation along the view);
     5064        - ``scale`` -- an integer specifying the scaling of the tikz picture;
     5065        - ``edge_color``, ``facet_color``, ``vertex_color`` -- string
     5066          representing colors which tikz recognize;
     5067        - ``opacity`` -- a real number between 0 and 1 giving the opacity of
     5068          the front facets;
     5069        - ``axis`` -- a Boolean, to draw the axes at the origin.
     5070
     5071        .. NOTE::
     5072
     5073            The ``facet_color`` is the filing color of the polytope (polygon).
     5074
     5075        """
     5076
     5077        V = VectorSpace(RDF,3)
     5078        view_vector = V(view)
     5079        rot = rotate_arbitrary(view_vector,-(rot_angle/360)*2*pi)
     5080        rotation_matrix = rot[:2].transpose()
     5081
     5082        # Creates the nodes, coordinate and tag for every vertex of the polytope.
     5083        # The tag is used to draw the front facets later on.
     5084        dict_drawing = {}
     5085        edges = ''
     5086        for v in self.Vrep_generator():
     5087            v_vect = str([i.n(digits=3) for i in v.vector()])
     5088            v_vect = v_vect.replace('[','(')
     5089            v_vect = v_vect.replace(']',')')
     5090            tag = '%s' %v_vect
     5091            node = "\\node[%s] at %s     {};\n" %('vertex',tag)
     5092
     5093            for nei in v.neighbors():
     5094                nei_vect = str([i.n(digits=3) for i in nei.vector()])
     5095                nei_vect = nei_vect.replace('[','(')
     5096                nei_vect = nei_vect.replace(']',')')
     5097                tag_nei = '%s' %nei_vect
     5098                edges += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei)
     5099
     5100            coord = '\coordinate %s at %s;\n' %(tag,tag)
     5101            dict_drawing[v] = node,coord,tag
     5102
     5103        # Start to write the output
     5104        tikz_pic = ''
     5105        tikz_pic += '\\begin{tikzpicture}%\n'
     5106        tikz_pic += '\t[x={(%fcm, %fcm)},\n' %(n(rotation_matrix[0][0]),n(rotation_matrix[0][1]))
     5107        tikz_pic += '\ty={(%fcm, %fcm)},\n' %(n(rotation_matrix[1][0]),n(rotation_matrix[1][1]))
     5108        tikz_pic += '\tz={(%fcm, %fcm)},\n' %(n(rotation_matrix[2][0]),n(rotation_matrix[2][1]))
     5109        tikz_pic += '\tscale=%f,\n' %scale
     5110        tikz_pic += '\tback/.style={loosely dotted, thin},\n'
     5111        tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color
     5112        tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity)
     5113        tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color
     5114        tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color
     5115
     5116        # Draws the axes if True
     5117        if axis:
     5118            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
     5119            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'
     5120            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'
     5121
     5122        # Create the coordinate of the vertices:
     5123        tikz_pic += '%% Coordinate of the vertices:\n\n'
     5124        for v in self.Vrep_generator():
     5125            tikz_pic += dict_drawing[v][1]
     5126
     5127        # Draw the interior by going in a cycle
     5128        tikz_pic += '\n\n%% Drawing the interior\n\n'
     5129        cyclic_vert = cyclic_sort_vertices_2d(list(self.Vrep_generator()))
     5130        tikz_pic += '\\fill[facet] '
     5131        for v in cyclic_vert:
     5132            tikz_pic += '%s -- ' %dict_drawing[v][2]
     5133        tikz_pic += 'cycle {};\n'
     5134
     5135        # Draw the edges in the front
     5136        tikz_pic += '\n\n%% Drawing edges in the front\n\n'
     5137        tikz_pic += edges
     5138
     5139        # Finally, the vertices in front are drawn on top of everything.
     5140        tikz_pic += '\n\n%% Drawing the vertices in the front\n\n'
     5141        for v in self.Vrep_generator():
     5142            tikz_pic += dict_drawing[v][0]
     5143        tikz_pic += '\n\n\\end{tikzpicture}'
     5144
     5145        return tikz_pic
     5146
     5147    def _tikz_3d_in_3d(self, view, rot_angle, scale, edge_color,
     5148                       facet_color, opacity, vertex_color, axis):
     5149        """
     5150        Return a string ``tikz_pic`` consisting of a tikz picture of ``self``
     5151        according to a projection ``view`` and an angle ``rot_angle``
     5152        obtained via Jmol through the current state property.
     5153
     5154        INPUT:
     5155
     5156        - ``view`` -- a list of length 3 representing a vector;
     5157        - ``rot_angle`` -- an angle in degree from 0 to 360;
     5158        - ``scale`` -- an integer specifying the scaling of the tikz picture;
     5159        - ``edge_color``, ``facet_color``, ``vertex_color`` -- string
     5160          representing colors which tikz recognize;
     5161        - ``opacity`` -- a real number between 0 and 1 giving the opacity of
     5162          the front facets;
     5163        - ``axis`` -- a Boolean, to draw the axes at the origin.
     5164
     5165        """
     5166        V = VectorSpace(RDF,3)
     5167        view_vector = V(view)
     5168        rot = rotate_arbitrary(view_vector,-(rot_angle/360)*2*pi)
     5169        rotation_matrix = rot[:2].transpose()
     5170        proj_vector = (rot**(-1))*V([0,0,1])
     5171
     5172        # First compute the back and front vertices and facets
     5173        front_facets = []
     5174        back_facets = []
     5175        for facet in self.Hrep_generator():
     5176            A = facet.vector()[1:]
     5177            B = facet.vector()[0]
     5178            if A*(2000*proj_vector)+B<0:
     5179                front_facets += [facet]
     5180            else:
     5181                back_facets += [facet]
     5182
     5183        front_vertices = []
     5184        for facet in front_facets:
     5185            A = facet.vector()[1:]
     5186            B = facet.vector()[0]
     5187            for v in self.Vrep_generator():
     5188                if A*v.vector()+B<0.0005:
     5189                    front_vertices += [v]
     5190
     5191        back_vertices = []
     5192        for facet in back_facets:
     5193            A = facet.vector()[1:]
     5194            B = facet.vector()[0]
     5195            for v in self.Vrep_generator():
     5196                if A*v.vector()+B<0.0005:
     5197                    back_vertices += [v]
     5198
     5199        # Creates the nodes, coordinate and tag for every vertex of the polytope.
     5200        # The tag is used to draw the front facets later on.
     5201        dict_drawing = {}
     5202        first_part = ''
     5203        second_part = ''
     5204        for v in self.Vrep_generator():
     5205            v_vect = str([i.n(digits=3) for i in v.vector()])
     5206            v_vect = v_vect.replace('[','(')
     5207            v_vect = v_vect.replace(']',')')
     5208            tag = '%s' %v_vect
     5209            node = "\\node[%s] at %s     {};\n" %('vertex',tag)
     5210            for nei in v.neighbors():
     5211                nei_vect = str([i.n(digits=3) for i in nei.vector()])
     5212                nei_vect = nei_vect.replace('[','(')
     5213                nei_vect = nei_vect.replace(']',')')
     5214                tag_nei = '%s' %nei_vect
     5215                H_v = set(v.incident())
     5216                H_nei = set(nei.incident())
     5217                H_v_nei = [h in back_facets for h in H_v.intersection(H_nei)]
     5218
     5219                # The back edge has to be between two vertices in the Back
     5220                # AND such that the 2 facets touching them are in the Back
     5221                if v in back_vertices and nei in back_vertices and sum(H_v_nei)==2:
     5222                    first_part += "\\draw[%s,back] %s -- %s;\n" %('edge',tag,tag_nei)
     5223                else:
     5224                    second_part += "\\draw[%s] %s -- %s;\n" %('edge',tag,tag_nei)
     5225            coord = '\coordinate %s at %s;\n' %(tag,tag)
     5226            dict_drawing[v] = node,coord,tag
     5227
     5228        # Start to write the output
     5229        tikz_pic = ''
     5230        tikz_pic += '\\begin{tikzpicture}%\n'
     5231        tikz_pic += '\t[x={(%fcm, %fcm)},\n' %(n(rotation_matrix[0][0]),n(rotation_matrix[0][1]))
     5232        tikz_pic += '\ty={(%fcm, %fcm)},\n' %(n(rotation_matrix[1][0]),n(rotation_matrix[1][1]))
     5233        tikz_pic += '\tz={(%fcm, %fcm)},\n' %(n(rotation_matrix[2][0]),n(rotation_matrix[2][1]))
     5234        tikz_pic += '\tscale=%f,\n' %scale
     5235        tikz_pic += '\tback/.style={loosely dotted, thin},\n'
     5236        tikz_pic += '\tedge/.style={color=%s, thick},\n' %edge_color
     5237        tikz_pic += '\tfacet/.style={fill=%s,fill opacity=%f},\n' %(facet_color,opacity)
     5238        tikz_pic += '\tvertex/.style={inner sep=1pt,circle,draw=%s!25!black,' %vertex_color
     5239        tikz_pic += 'fill=%s!75!black,thick,anchor=base}]\n\n\n' %vertex_color
     5240
     5241        # Draws the axes if True
     5242        if axis:
     5243            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (1,0,0) node[anchor=north east]{$x$};\n'
     5244            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,1,0) node[anchor=north west]{$y$};\n'
     5245            tikz_pic += '\\draw[color=black,thick,->] (0,0,0) -- (0,0,1) node[anchor=south]{$z$};\n'
     5246
     5247        # Create the coordinate of the vertices
     5248        tikz_pic += '%% Coordinate of the vertices:\n\n'
     5249        for v in self.Vrep_generator():
     5250            tikz_pic += dict_drawing[v][1]
     5251
     5252        # Draw the edges in the back
     5253        tikz_pic += '\n\n%% Drawing edges in the back\n\n'
     5254        tikz_pic += first_part
     5255
     5256        # Draw the vertices on top of the back-edges
     5257        tikz_pic += '\n\n%% Drawing vertices in the back\n\n'
     5258        for v in self.Vrep_generator():
     5259            if v in back_vertices and v not in front_vertices:
     5260                tikz_pic += dict_drawing[v][0]
     5261
     5262        # Draw the facets in the front by going in cycles for every facet.
     5263        tikz_pic += '\n\n%% Drawing the facets\n\n'
     5264        for facet in front_facets:
     5265            cyclic_vert = cyclic_sort_vertices_2d(list(facet.incident()))
     5266            tikz_pic += '\\fill[facet] '
     5267            for v in cyclic_vert:
     5268                tikz_pic += '%s -- ' %dict_drawing[v][2]
     5269            tikz_pic += 'cycle {};\n'
     5270
     5271        # Draw the edges in the front
     5272        tikz_pic += '\n\n%% Drawing edges in the front\n\n'
     5273        tikz_pic += second_part
     5274
     5275        # Finally, the vertices in front are drawn on top of everything.
     5276        tikz_pic += '\n\n%% Drawing the vertices in the front\n\n'
     5277        for v in self.Vrep_generator():
     5278            if v in front_vertices:
     5279                tikz_pic += dict_drawing[v][0]
     5280        tikz_pic += '\n\n\\end{tikzpicture}'
     5281
     5282        return tikz_pic
    48785283
    48795284#############################################################
    48805285def cyclic_sort_vertices_2d(Vlist):